Offer restricted access to DataBuffer's ByteBuffer

This commit introduces DataBuffer::readableByteBuffers and
DataBuffer::writableByteBuffers, allowing restricted access to the
ByteBuffer used internally by DataBuffer implementations.

Closes gh-29943
This commit is contained in:
Arjen Poutsma
2022-11-30 12:30:10 +01:00
parent 72926c29f9
commit 3e2f58cdd2
28 changed files with 773 additions and 275 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -17,7 +17,6 @@
package org.springframework.web.reactive.resource;
import java.io.StringWriter;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -88,9 +87,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
.read(outputResource, bufferFactory, StreamUtils.BUFFER_SIZE);
return DataBufferUtils.join(flux)
.flatMap(dataBuffer -> {
CharBuffer charBuffer = DEFAULT_CHARSET.decode(dataBuffer.asByteBuffer());
String cssContent = dataBuffer.toString(DEFAULT_CHARSET);
DataBufferUtils.release(dataBuffer);
String cssContent = charBuffer.toString();
return transformContent(cssContent, outputResource, transformerChain, exchange);
});
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -20,12 +20,14 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.WriteCallback;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -84,24 +86,28 @@ public class JettyWebSocketSession extends AbstractListenerWebSocketSession<Sess
@Override
protected boolean sendMessage(WebSocketMessage message) throws IOException {
ByteBuffer buffer = message.getPayload().toByteBuffer();
DataBuffer dataBuffer = message.getPayload();
RemoteEndpoint remote = getDelegate().getRemote();
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);
getDelegate().getRemote().sendString(text, new SendProcessorCallback());
}
else if (WebSocketMessage.Type.BINARY.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
getDelegate().getRemote().sendBytes(buffer, new SendProcessorCallback());
}
else if (WebSocketMessage.Type.PING.equals(message.getType())) {
getDelegate().getRemote().sendPing(buffer);
}
else if (WebSocketMessage.Type.PONG.equals(message.getType())) {
getDelegate().getRemote().sendPong(buffer);
String text = dataBuffer.toString(StandardCharsets.UTF_8);
remote.sendString(text, new SendProcessorCallback());
}
else {
throw new IllegalArgumentException("Unexpected message type: " + message.getType());
if (WebSocketMessage.Type.BINARY.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
}
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
while (iterator.hasNext()) {
ByteBuffer byteBuffer = iterator.next();
switch (message.getType()) {
case BINARY -> remote.sendBytes(byteBuffer, new SendProcessorCallback());
case PING -> remote.sendPing(byteBuffer);
case PONG -> remote.sendPong(byteBuffer);
default -> throw new IllegalArgumentException("Unexpected message type: " + message.getType());
}
}
}
}
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -22,12 +22,14 @@ import java.nio.charset.StandardCharsets;
import jakarta.websocket.CloseReason;
import jakarta.websocket.CloseReason.CloseCodes;
import jakarta.websocket.RemoteEndpoint;
import jakarta.websocket.SendHandler;
import jakarta.websocket.SendResult;
import jakarta.websocket.Session;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.socket.CloseStatus;
@@ -73,24 +75,28 @@ public class StandardWebSocketSession extends AbstractListenerWebSocketSession<S
@Override
protected boolean sendMessage(WebSocketMessage message) throws IOException {
ByteBuffer buffer = message.getPayload().toByteBuffer();
DataBuffer dataBuffer = message.getPayload();
RemoteEndpoint.Async remote = getDelegate().getAsyncRemote();
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);
getDelegate().getAsyncRemote().sendText(text, new SendProcessorCallback());
}
else if (WebSocketMessage.Type.BINARY.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
getDelegate().getAsyncRemote().sendBinary(buffer, new SendProcessorCallback());
}
else if (WebSocketMessage.Type.PING.equals(message.getType())) {
getDelegate().getAsyncRemote().sendPing(buffer);
}
else if (WebSocketMessage.Type.PONG.equals(message.getType())) {
getDelegate().getAsyncRemote().sendPong(buffer);
String text = dataBuffer.toString(StandardCharsets.UTF_8);
remote.sendText(text, new SendProcessorCallback());
}
else {
throw new IllegalArgumentException("Unexpected message type: " + message.getType());
if (WebSocketMessage.Type.BINARY.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
}
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
while (iterator.hasNext()) {
ByteBuffer byteBuffer = iterator.next();
switch (message.getType()) {
case BINARY -> remote.sendBinary(byteBuffer, new SendProcessorCallback());
case PING -> remote.sendPing(byteBuffer);
case PONG -> remote.sendPong(byteBuffer);
default -> throw new IllegalArgumentException("Unexpected message type: " + message.getType());
}
}
}
}
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -76,26 +76,26 @@ public class UndertowWebSocketSession extends AbstractListenerWebSocketSession<W
@Override
protected boolean sendMessage(WebSocketMessage message) throws IOException {
ByteBuffer buffer = message.getPayload().toByteBuffer();
DataBuffer dataBuffer = message.getPayload();
WebSocketChannel channel = getDelegate();
if (WebSocketMessage.Type.TEXT.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
String text = new String(buffer.array(), StandardCharsets.UTF_8);
WebSockets.sendText(text, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else if (WebSocketMessage.Type.BINARY.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
WebSockets.sendBinary(buffer, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else if (WebSocketMessage.Type.PING.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
WebSockets.sendPing(buffer, getDelegate(), new SendProcessorCallback(message.getPayload()));
}
else if (WebSocketMessage.Type.PONG.equals(message.getType())) {
getSendProcessor().setReadyToSend(false);
WebSockets.sendPong(buffer, getDelegate(), new SendProcessorCallback(message.getPayload()));
String text = dataBuffer.toString(StandardCharsets.UTF_8);
WebSockets.sendText(text, channel, new SendProcessorCallback(message.getPayload()));
}
else {
throw new IllegalArgumentException("Unexpected message type: " + message.getType());
getSendProcessor().setReadyToSend(false);
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
while (iterator.hasNext()) {
ByteBuffer byteBuffer = iterator.next();
switch (message.getType()) {
case BINARY -> WebSockets.sendBinary(byteBuffer, channel, new SendProcessorCallback(dataBuffer));
case PING -> WebSockets.sendPing(byteBuffer, channel, new SendProcessorCallback(dataBuffer));
case PONG -> WebSockets.sendPong(byteBuffer, channel, new SendProcessorCallback(dataBuffer));
default -> throw new IllegalArgumentException("Unexpected message type: " + message.getType());
}
}
}
}
return true;
}