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:
@@ -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.
|
||||
@@ -102,7 +102,11 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
@Override
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
return doCommit(() -> {
|
||||
this.byteBufferFlux = Flux.from(body).map(DataBuffer::toByteBuffer);
|
||||
this.byteBufferFlux = Flux.from(body).map(dataBuffer -> {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBuffer.readableByteCount());
|
||||
dataBuffer.toByteBuffer(byteBuffer);
|
||||
return byteBuffer;
|
||||
});
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -122,8 +122,8 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
private HttpRequest.BodyPublisher toBodyPublisher(Publisher<? extends DataBuffer> body) {
|
||||
Publisher<ByteBuffer> byteBufferBody = (body instanceof Mono ?
|
||||
Mono.from(body).map(DataBuffer::toByteBuffer) :
|
||||
Flux.from(body).map(DataBuffer::toByteBuffer));
|
||||
Mono.from(body).map(this::toByteBuffer) :
|
||||
Flux.from(body).map(this::toByteBuffer));
|
||||
|
||||
Flow.Publisher<ByteBuffer> bodyFlow = JdkFlowAdapter.publisherToFlowPublisher(byteBufferBody);
|
||||
|
||||
@@ -132,6 +132,12 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
||||
HttpRequest.BodyPublishers.fromPublisher(bodyFlow));
|
||||
}
|
||||
|
||||
private ByteBuffer toByteBuffer(DataBuffer dataBuffer) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBuffer.readableByteCount());
|
||||
dataBuffer.toByteBuffer(byteBuffer);
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> writeAndFlushWith(final Publisher<? extends Publisher<? extends DataBuffer>> body) {
|
||||
return writeWith(Flux.from(body).flatMap(Function.identity()));
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.http.client.reactive;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -109,15 +110,17 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
||||
return contentType != null ? contentType.toString() : MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
}
|
||||
|
||||
private ContentChunk toContentChunk(DataBuffer buffer, MonoSink<Void> sink) {
|
||||
return new ContentChunk(buffer.toByteBuffer(), new Callback() {
|
||||
private ContentChunk toContentChunk(DataBuffer dataBuffer, MonoSink<Void> sink) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBuffer.readableByteCount());
|
||||
dataBuffer.toByteBuffer(byteBuffer);
|
||||
return new ContentChunk(byteBuffer, new Callback() {
|
||||
@Override
|
||||
public void succeeded() {
|
||||
DataBufferUtils.release(buffer);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable t) {
|
||||
DataBufferUtils.release(buffer);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
sink.error(t);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
@@ -93,8 +92,11 @@ final class Jackson2Tokenizer {
|
||||
try {
|
||||
int bufferSize = dataBuffer.readableByteCount();
|
||||
if (this.inputFeeder instanceof ByteBufferFeeder byteBufferFeeder) {
|
||||
ByteBuffer byteBuffer = dataBuffer.toByteBuffer();
|
||||
byteBufferFeeder.feedInput(byteBuffer);
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext()) {
|
||||
byteBufferFeeder.feedInput(iterator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.inputFeeder instanceof ByteArrayFeeder byteArrayFeeder) {
|
||||
byte[] bytes = new byte[bufferSize];
|
||||
|
||||
@@ -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.
|
||||
@@ -747,9 +747,13 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
|
||||
@SuppressWarnings("BlockingMethodInNonBlockingContext")
|
||||
private Mono<Void> writeInternal(DataBuffer dataBuffer) {
|
||||
try {
|
||||
ByteBuffer byteBuffer = dataBuffer.toByteBuffer();
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
this.channel.write(byteBuffer);
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
this.channel.write(byteBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -151,8 +151,9 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Mes
|
||||
|
||||
try {
|
||||
Message.Builder builder = getMessageBuilder(targetType.toClass());
|
||||
ByteBuffer buffer = dataBuffer.toByteBuffer();
|
||||
builder.mergeFrom(CodedInputStream.newInstance(buffer), this.extensionRegistry);
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBuffer.readableByteCount());
|
||||
dataBuffer.toByteBuffer(byteBuffer);
|
||||
builder.mergeFrom(CodedInputStream.newInstance(byteBuffer), this.extensionRegistry);
|
||||
return builder.build();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
@@ -236,7 +237,9 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Mes
|
||||
this.messageBytesToRead -= chunkBytesToRead;
|
||||
|
||||
if (this.messageBytesToRead == 0) {
|
||||
CodedInputStream stream = CodedInputStream.newInstance(this.output.toByteBuffer());
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(this.output.readableByteCount());
|
||||
this.output.toByteBuffer(byteBuffer);
|
||||
CodedInputStream stream = CodedInputStream.newInstance(byteBuffer);
|
||||
DataBufferUtils.release(this.output);
|
||||
this.output = null;
|
||||
Message message = getMessageBuilder(this.elementType.toClass())
|
||||
|
||||
@@ -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.
|
||||
@@ -181,7 +181,12 @@ public class XmlEventDecoder extends AbstractDecoder<XMLEvent> {
|
||||
public List<? extends XMLEvent> apply(DataBuffer dataBuffer) {
|
||||
try {
|
||||
increaseByteCount(dataBuffer);
|
||||
this.streamReader.getInputFeeder().feedInput(dataBuffer.toByteBuffer());
|
||||
AsyncByteBufferFeeder inputFeeder = this.streamReader.getInputFeeder();
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext()) {
|
||||
inputFeeder.feedInput(iterator.next());
|
||||
}
|
||||
}
|
||||
List<XMLEvent> events = new ArrayList<>();
|
||||
while (true) {
|
||||
if (this.streamReader.next() == AsyncXMLStreamReader.EVENT_INCOMPLETE) {
|
||||
|
||||
@@ -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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.http.server.reactive;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -158,11 +157,15 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
|
||||
@Override
|
||||
protected int writeToOutputStream(DataBuffer dataBuffer) throws IOException {
|
||||
OutputStream output = getOutputStream();
|
||||
if (output instanceof HttpOutput httpOutput) {
|
||||
ByteBuffer input = dataBuffer.toByteBuffer();
|
||||
int len = input.remaining();
|
||||
httpOutput.write(input);
|
||||
if (getOutputStream() instanceof HttpOutput httpOutput) {
|
||||
int len = 0;
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext() && httpOutput.isReady()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
len += byteBuffer.remaining();
|
||||
httpOutput.write(byteBuffer);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return super.writeToOutputStream(dataBuffer);
|
||||
|
||||
@@ -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.
|
||||
@@ -35,6 +35,7 @@ import org.apache.coyote.Response;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -125,26 +126,38 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
|
||||
@Override
|
||||
protected DataBuffer readFromInputStream() throws IOException {
|
||||
if (!(getInputStream() instanceof CoyoteInputStream coyoteInputStream)) {
|
||||
if (getInputStream() instanceof CoyoteInputStream coyoteInputStream) {
|
||||
DataBuffer dataBuffer = this.factory.allocateBuffer(this.bufferSize);
|
||||
int read = -1;
|
||||
try {
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.writableByteBuffers()) {
|
||||
Assert.state(iterator.hasNext(), "No ByteBuffer available");
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
read = coyoteInputStream.read(byteBuffer);
|
||||
}
|
||||
logBytesRead(read);
|
||||
if (read > 0) {
|
||||
dataBuffer.writePosition(read);
|
||||
return dataBuffer;
|
||||
}
|
||||
else if (read == -1) {
|
||||
return EOF_BUFFER;
|
||||
}
|
||||
else {
|
||||
return AbstractListenerReadPublisher.EMPTY_BUFFER;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (read <= 0) {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// It's possible InputStream can be wrapped, preventing use of CoyoteInputStream
|
||||
return super.readFromInputStream();
|
||||
}
|
||||
|
||||
ByteBuffer byteBuffer = this.factory.isDirect() ?
|
||||
ByteBuffer.allocateDirect(this.bufferSize) :
|
||||
ByteBuffer.allocate(this.bufferSize);
|
||||
|
||||
int read = coyoteInputStream.read(byteBuffer);
|
||||
logBytesRead(read);
|
||||
if (read > 0) {
|
||||
return this.factory.wrap(byteBuffer);
|
||||
}
|
||||
else if (read == -1) {
|
||||
return EOF_BUFFER;
|
||||
}
|
||||
else {
|
||||
return AbstractListenerReadPublisher.EMPTY_BUFFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +210,20 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
|
||||
@Override
|
||||
protected int writeToOutputStream(DataBuffer dataBuffer) throws IOException {
|
||||
if (!(getOutputStream() instanceof CoyoteOutputStream coyoteOutputStream)) {
|
||||
if (getOutputStream() instanceof CoyoteOutputStream coyoteOutputStream) {
|
||||
int len = 0;
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
while (iterator.hasNext() && coyoteOutputStream.isReady()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
len += byteBuffer.remaining();
|
||||
coyoteOutputStream.write(byteBuffer);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
return super.writeToOutputStream(dataBuffer);
|
||||
}
|
||||
|
||||
ByteBuffer input = dataBuffer.toByteBuffer();
|
||||
int len = input.remaining();
|
||||
coyoteOutputStream.write(input);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -230,7 +230,9 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
|
||||
@Override
|
||||
protected void dataReceived(DataBuffer dataBuffer) {
|
||||
super.dataReceived(dataBuffer);
|
||||
this.byteBuffer = dataBuffer.toByteBuffer();
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBuffer.readableByteCount());
|
||||
dataBuffer.toByteBuffer(byteBuffer);
|
||||
this.byteBuffer = byteBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user