Skip to content

Commit

Permalink
Allows the webserver's write buffer size to be set to 0 (#9313)
Browse files Browse the repository at this point in the history
* Allows the webserver's write buffer size to be set to 0 (or less). The underlying output stream shall not be wrapped into a buffered output stream one unless this value is greater than 0.

* Improves Javadoc and renames same variables for better code readability.
  • Loading branch information
spericas authored Oct 2, 2024
1 parent 1127d96 commit 5a6ecd1
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>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);
}
Expand Down

0 comments on commit 5a6ecd1

Please sign in to comment.