From 2f2c49c6746af464d4be879501a73ed2311df8db Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 13 Nov 2024 11:02:08 +0100 Subject: [PATCH 1/2] faster and simpler HttpOutput.print Signed-off-by: Ludovic Orban --- .../jetty/ee11/servlet/HttpOutput.java | 117 +++++++----------- 1 file changed, 47 insertions(+), 70 deletions(-) diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java index 2e8987a67b31..0d7272005df2 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java @@ -16,13 +16,10 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritePendingException; -import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; -import java.nio.charset.CoderResult; -import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; import java.util.concurrent.CancellationException; import jakarta.servlet.RequestDispatcher; @@ -722,6 +719,17 @@ private void checkWritable() throws EofException @Override public void write(byte[] b, int off, int len) throws IOException + { + write(b, off, len, null); + } + + @FunctionalInterface + private interface IORunnable + { + void run() throws IOException; + } + + private void write(byte[] b, int off, int len, IORunnable onComplete) throws IOException { if (LOG.isDebugEnabled()) LOG.debug("write(array {})", BufferUtil.toDetailString(ByteBuffer.wrap(b, off, len))); @@ -784,6 +792,8 @@ public void write(byte[] b, int off, int len) throws IOException if (LOG.isDebugEnabled()) LOG.debug("write(array) {} aggregated !flush {}", stateString(), _aggregate); + if (onComplete != null) + onComplete.run(); return; } @@ -799,8 +809,27 @@ public void write(byte[] b, int off, int len) throws IOException if (async) { + IORunnable copy = onComplete; // Do the asynchronous writing from the callback - new AsyncWrite(b, off, len, last).iterate(); + new AsyncWrite(b, off, len, last) + { + @Override + protected void onCompleted(Throwable causeOrNull) + { + if (copy != null) + { + try + { + copy.run(); + } + catch (Throwable x) + { + causeOrNull = ExceptionUtil.combine(causeOrNull, x); + } + } + super.onCompleted(causeOrNull); + } + }.iterate(); return; } @@ -820,6 +849,10 @@ public void write(byte[] b, int off, int len) throws IOException if (len > 0 && !last && len <= _commitSize && len <= maximizeAggregateSpace()) { BufferUtil.append(byteBuffer, b, off, len); + IORunnable copy = onComplete; + onComplete = null; + if (copy != null) + copy.run(); onWriteComplete(false, null); return; } @@ -849,10 +882,16 @@ else if (last && !complete) channelWrite(BufferUtil.EMPTY_BUFFER, true); } + IORunnable copy = onComplete; + onComplete = null; + if (copy != null) + copy.run(); onWriteComplete(last, null); } catch (Throwable t) { + if (onComplete != null) + onComplete.run(); onWriteComplete(last, t); throw t; } @@ -1026,72 +1065,10 @@ private void print(String s, boolean eoln) throws IOException s = String.valueOf(s); String charset = _servletChannel.getServletContextResponse().getCharacterEncoding(false); - CharsetEncoder encoder = _encoder.take(); - if (encoder == null || !encoder.charset().name().equalsIgnoreCase(charset)) - { - encoder = Charset.forName(charset).newEncoder(); - encoder.onMalformedInput(CodingErrorAction.REPLACE); - encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); - } - ByteBufferPool pool = _servletChannel.getRequest().getComponents().getByteBufferPool(); - RetainableByteBuffer out = pool.acquire((int)(1 + (s.length() + 2) * encoder.averageBytesPerChar()), false); - try - { - CharBuffer in = CharBuffer.wrap(s); - CharBuffer crlf = eoln ? CharBuffer.wrap("\r\n") : null; - ByteBuffer byteBuffer = out.getByteBuffer(); - BufferUtil.flipToFill(byteBuffer); - while (true) - { - CoderResult result; - if (in.hasRemaining()) - { - result = encoder.encode(in, byteBuffer, crlf == null); - if (result.isUnderflow()) - if (crlf == null) - break; - else - continue; - } - else if (crlf != null && crlf.hasRemaining()) - { - result = encoder.encode(crlf, byteBuffer, true); - if (result.isUnderflow()) - { - if (!encoder.flush(byteBuffer).isUnderflow()) - result.throwException(); - break; - } - } - else - break; - - if (result.isOverflow()) - { - BufferUtil.flipToFlush(byteBuffer, 0); - RetainableByteBuffer bigger = pool.acquire(out.capacity() + s.length() + 2, out.isDirect()); - BufferUtil.flipToFill(bigger.getByteBuffer()); - bigger.getByteBuffer().put(byteBuffer); - out.release(); - BufferUtil.flipToFill(bigger.getByteBuffer()); - out = bigger; - byteBuffer = bigger.getByteBuffer(); - continue; - } - - result.throwException(); - } - - BufferUtil.flipToFlush(byteBuffer, 0); - write(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.remaining()); - } - finally - { - out.release(); - encoder.reset(); - _encoder.offer(encoder); - } + byte[] bytes = s.getBytes(charset); + IORunnable r = eoln ? () -> write("\r\n".getBytes(StandardCharsets.UTF_8)) : null; + write(bytes, 0, bytes.length, r); } /** From de9530e7eab81719135c714c9bcf47be10552a24 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 15 Nov 2024 14:34:04 +0100 Subject: [PATCH 2/2] better variable names Signed-off-by: Ludovic Orban --- .../jetty/ee11/servlet/HttpOutput.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java index 0d7272005df2..898f70143dfc 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java @@ -19,7 +19,6 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritePendingException; import java.nio.charset.CharsetEncoder; -import java.nio.charset.StandardCharsets; import java.util.concurrent.CancellationException; import jakarta.servlet.RequestDispatcher; @@ -809,18 +808,18 @@ private void write(byte[] b, int off, int len, IORunnable onComplete) throws IOE if (async) { - IORunnable copy = onComplete; + IORunnable finalOnComplete = onComplete; // Do the asynchronous writing from the callback new AsyncWrite(b, off, len, last) { @Override protected void onCompleted(Throwable causeOrNull) { - if (copy != null) + if (finalOnComplete != null) { try { - copy.run(); + finalOnComplete.run(); } catch (Throwable x) { @@ -849,10 +848,10 @@ protected void onCompleted(Throwable causeOrNull) if (len > 0 && !last && len <= _commitSize && len <= maximizeAggregateSpace()) { BufferUtil.append(byteBuffer, b, off, len); - IORunnable copy = onComplete; + IORunnable runOnComplete = onComplete; onComplete = null; - if (copy != null) - copy.run(); + if (runOnComplete != null) + runOnComplete.run(); onWriteComplete(false, null); return; } @@ -882,10 +881,10 @@ else if (last && !complete) channelWrite(BufferUtil.EMPTY_BUFFER, true); } - IORunnable copy = onComplete; + IORunnable runOnComplete = onComplete; onComplete = null; - if (copy != null) - copy.run(); + if (runOnComplete != null) + runOnComplete.run(); onWriteComplete(last, null); } catch (Throwable t) @@ -1067,8 +1066,8 @@ private void print(String s, boolean eoln) throws IOException String charset = _servletChannel.getServletContextResponse().getCharacterEncoding(false); byte[] bytes = s.getBytes(charset); - IORunnable r = eoln ? () -> write("\r\n".getBytes(StandardCharsets.UTF_8)) : null; - write(bytes, 0, bytes.length, r); + IORunnable onComplete = eoln ? () -> write(IO.CRLF_BYTES) : null; + write(bytes, 0, bytes.length, onComplete); } /**