diff --git a/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java b/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java index 114619a70eb..5acf12c4a75 100644 --- a/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java +++ b/microprofile/server/src/main/java/io/helidon/microprofile/server/JaxRsService.java @@ -335,7 +335,8 @@ public OutputStream writeResponseStatusAndHeaders(long contentLengthParam, if (contentLength > 0) { res.header(HeaderValues.create(HeaderNames.CONTENT_LENGTH, String.valueOf(contentLength))); } - this.outputStream = res.outputStream(); + // in case there is an exception during close operation, we would lose the information and wait indefinitely + this.outputStream = new ReleaseLatchStream(cdl, res.outputStream()); return outputStream; } @@ -364,6 +365,10 @@ public void commit() { } catch (IOException e) { cdl.countDown(); throw new UncheckedIOException(e); + } catch (Throwable e) { + // always release on commit, regardless of what happened + cdl.countDown(); + throw e; } } @@ -382,7 +387,7 @@ public boolean enableResponseBuffering() { return true; // enable buffering in Jersey } - public void await() { + void await() { try { cdl.await(); } catch (InterruptedException e) { @@ -391,6 +396,45 @@ public void await() { } } + private static class ReleaseLatchStream extends OutputStream { + private final CountDownLatch cdl; + private final OutputStream delegate; + + private ReleaseLatchStream(CountDownLatch cdl, OutputStream delegate) { + this.cdl = cdl; + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + delegate.write(b, off, len); + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() throws IOException { + try { + delegate.close(); + } finally { + cdl.countDown(); + } + } + } + private static class BaseUriRequestUri { private final URI baseUri; private final URI requestUri; 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 ea41127d14c..03abb2ce312 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 @@ -19,6 +19,7 @@ import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Objects; @@ -843,7 +844,7 @@ public void close() { closingDelegate.closing(); // inform of imminent call to close for last flush try { delegate.close(); - } catch (IOException e) { + } catch (IOException | UncheckedIOException e) { throw new ServerConnectionException("Failed to close server output stream", e); } } @@ -856,7 +857,7 @@ void commit() { try { flush(); closingDelegate.commit(); - } catch (IOException e) { + } catch (IOException | UncheckedIOException e) { throw new ServerConnectionException("Failed to flush server output stream", e); } }