diff --git a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamBufferSizeTest.java b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamBufferSizeTest.java new file mode 100644 index 00000000000..966e1a54549 --- /dev/null +++ b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamBufferSizeTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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.helidon.webserver.tests; + +import io.helidon.webclient.http1.Http1Client; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.testing.junit5.ServerTest; +import io.helidon.webserver.testing.junit5.SetUpServer; + +@ServerTest +class OutputStreamBufferSizeTest extends OutputStreamTest { + + OutputStreamBufferSizeTest(Http1Client client) { + super(client); + } + + @SetUpServer + static void setup(WebServerConfig.Builder builder) { + builder.writeBufferSize(0); // should accept buffer size of 0 + } +} diff --git a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamTest.java b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamTest.java index 2411e420644..3733604eda5 100644 --- a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamTest.java +++ b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/OutputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Oracle and/or its affiliates. + * Copyright (c) 2022, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ void verifyAutoFlush() { } } - private static class Service { + protected static class Service { public static void outputStream(ServerRequest req, ServerResponse res) throws IOException { InputStream in = new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8)); diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/ListenerConfigBlueprint.java b/webserver/webserver/src/main/java/io/helidon/webserver/ListenerConfigBlueprint.java index 0894d00bc8c..c2fe1034214 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/ListenerConfigBlueprint.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/ListenerConfigBlueprint.java @@ -175,7 +175,8 @@ interface ListenerConfigBlueprint { /** * Initial buffer size in bytes of {@link java.io.BufferedOutputStream} created internally to - * write data to a socket connection. Default is {@code 4096}. + * write data to a socket connection. Default is {@code 4096}. Set buffer size to a value + * less than one to turn off buffering. * * @return initial buffer size used for writing */ diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerResponse.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerResponse.java index 330611ae76b..3f2804da792 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerResponse.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1ServerResponse.java @@ -753,55 +753,59 @@ private void writeContent(BufferData buffer) throws IOException { * A special stream that provides buffering for a delegate and special handling * of close logic. Note that due to some locking issues in the JDK, this class * must use delegation with {@link BufferedOutputStream} instead of subclassing. + * + *

If the buffer size is less or equal to zero, it will not wrap the + * {@link io.helidon.webserver.http1.Http1ServerResponse.BlockingOutputStream} + * with a {@link java.io.BufferedOutputStream}. */ static class ClosingBufferedOutputStream extends OutputStream { - private final BlockingOutputStream delegate; - private final BufferedOutputStream bufferedDelegate; + private final BlockingOutputStream closingDelegate; + private final OutputStream delegate; ClosingBufferedOutputStream(BlockingOutputStream out, int size) { - this.delegate = out; - this.bufferedDelegate = new BufferedOutputStream(out, size); + this.closingDelegate = out; + this.delegate = size <= 0 ? out : new BufferedOutputStream(out, size); } @Override public void write(int b) throws IOException { - bufferedDelegate.write(b); + delegate.write(b); } @Override public void write(byte[] b) throws IOException { - bufferedDelegate.write(b); + delegate.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { - bufferedDelegate.write(b, off, len); + delegate.write(b, off, len); } @Override public void flush() throws IOException { - bufferedDelegate.flush(); + delegate.flush(); } @Override public void close() { - delegate.closing(); // inform of imminent call to close for last flush + closingDelegate.closing(); // inform of imminent call to close for last flush try { - bufferedDelegate.close(); + delegate.close(); } catch (IOException e) { throw new ServerConnectionException("Failed to close server output stream", e); } } long totalBytesWritten() { - return delegate.totalBytesWritten(); + return closingDelegate.totalBytesWritten(); } void commit() { try { flush(); - delegate.commit(); + closingDelegate.commit(); } catch (IOException e) { throw new ServerConnectionException("Failed to flush server output stream", e); }