Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 77cd88b

Browse files
committedMar 18, 2025··
simplify + add extra test
Signed-off-by: Ludovic Orban <[email protected]>
1 parent 2a37779 commit 77cd88b

File tree

2 files changed

+74
-51
lines changed

2 files changed

+74
-51
lines changed
 

‎jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/HttpOutput.java

+39-51
Original file line numberDiff line numberDiff line change
@@ -802,17 +802,6 @@ private void checkWritable() throws EofException
802802

803803
@Override
804804
public void write(byte[] b, int off, int len) throws IOException
805-
{
806-
write(b, off, len, null);
807-
}
808-
809-
@FunctionalInterface
810-
private interface IORunnable
811-
{
812-
void run() throws IOException;
813-
}
814-
815-
private void write(byte[] b, int off, int len, IORunnable onComplete) throws IOException
816805
{
817806
if (LOG.isDebugEnabled())
818807
LOG.debug("write(array {})", BufferUtil.toDetailString(ByteBuffer.wrap(b, off, len)));
@@ -878,8 +867,6 @@ private void write(byte[] b, int off, int len, IORunnable onComplete) throws IOE
878867
if (LOG.isDebugEnabled())
879868
LOG.debug("write(array) {} aggregated !flush {}",
880869
lockedStateString(), _aggregate);
881-
if (onComplete != null)
882-
onComplete.run();
883870
return;
884871
}
885872

@@ -895,27 +882,8 @@ private void write(byte[] b, int off, int len, IORunnable onComplete) throws IOE
895882

896883
if (async)
897884
{
898-
IORunnable finalOnComplete = onComplete;
899885
// Do the asynchronous writing from the callback
900-
new AsyncWrite(b, off, len, last)
901-
{
902-
@Override
903-
protected void onCompleted(Throwable causeOrNull)
904-
{
905-
if (finalOnComplete != null)
906-
{
907-
try
908-
{
909-
finalOnComplete.run();
910-
}
911-
catch (Throwable x)
912-
{
913-
causeOrNull = ExceptionUtil.combine(causeOrNull, x);
914-
}
915-
}
916-
super.onCompleted(causeOrNull);
917-
}
918-
}.iterate();
886+
new AsyncWrite(b, off, len, last).iterate();
919887
return;
920888
}
921889

@@ -936,12 +904,6 @@ protected void onCompleted(Throwable causeOrNull)
936904
{
937905
BufferUtil.append(byteBuffer, b, off, len);
938906
onWriteComplete(false, null);
939-
if (onComplete != null)
940-
{
941-
IORunnable runOnComplete = onComplete;
942-
onComplete = null;
943-
runOnComplete.run();
944-
}
945907
return;
946908
}
947909
}
@@ -971,18 +933,10 @@ else if (last && !complete)
971933
}
972934

973935
onWriteComplete(last, null);
974-
if (onComplete != null)
975-
{
976-
IORunnable runOnComplete = onComplete;
977-
onComplete = null;
978-
runOnComplete.run();
979-
}
980936
}
981937
catch (Throwable t)
982938
{
983939
onWriteComplete(last, t);
984-
if (onComplete != null)
985-
onComplete.run();
986940
throw t;
987941
}
988942
}
@@ -1159,12 +1113,46 @@ private void print(String s, boolean eoln) throws IOException
11591113
throw new IOException("Closed");
11601114

11611115
s = String.valueOf(s);
1162-
11631116
String charset = _servletChannel.getServletContextResponse().getCharacterEncoding(false);
1164-
1117+
// String.getBytes() is much faster than a CharsetEncoder.encode() loop that would also need to estimate the
1118+
// destination buffer size, so this compensates for the rare extra copy that needs to be done when the aggregation
1119+
// buffer is full.
11651120
byte[] bytes = s.getBytes(charset);
1166-
IORunnable onComplete = eoln ? () -> write(IO.CRLF_BYTES) : null;
1167-
write(bytes, 0, bytes.length, onComplete);
1121+
if (!eoln)
1122+
{
1123+
write(bytes);
1124+
}
1125+
else
1126+
{
1127+
int len = bytes.length + IO.CRLF_BYTES.length;
1128+
1129+
// If there is enough room left in the aggregation buffer, just fill it otherwise copy into a bigger byte array.
1130+
boolean canAggregate = false;
1131+
try (AutoLock ignored = _channelState.lock())
1132+
{
1133+
if (len <= _commitSize)
1134+
{
1135+
lockedAcquireBuffer();
1136+
if (len <= maximizeAggregateSpace())
1137+
{
1138+
BufferUtil.fill(_aggregate.getByteBuffer(), bytes, 0, bytes.length);
1139+
canAggregate = true;
1140+
}
1141+
}
1142+
}
1143+
1144+
if (canAggregate)
1145+
{
1146+
write(IO.CRLF_BYTES);
1147+
}
1148+
else
1149+
{
1150+
byte[] bytesWithCrLf = new byte[len];
1151+
System.arraycopy(bytes, 0, bytesWithCrLf, 0, bytes.length);
1152+
System.arraycopy(IO.CRLF_BYTES, 0, bytesWithCrLf, bytes.length, IO.CRLF_BYTES.length);
1153+
write(bytesWithCrLf);
1154+
}
1155+
}
11681156
}
11691157

11701158
/**

‎jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/HttpOutputTest.java

+35
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,41 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
10581058
assertThat(response.replace("\r\n", System.lineSeparator()), containsString(expected));
10591059
}
10601060

1061+
@Test
1062+
public void testPrintlnSmallBuffer() throws Exception
1063+
{
1064+
ByteArrayOutputStream bout = new ByteArrayOutputStream();
1065+
PrintWriter exp = new PrintWriter(bout, true, StandardCharsets.UTF_8);
1066+
HttpServlet servlet = new HttpServlet()
1067+
{
1068+
@Override
1069+
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
1070+
{
1071+
response.setCharacterEncoding("UTF-8");
1072+
HttpOutput out = (HttpOutput)response.getOutputStream();
1073+
1074+
exp.println("a".repeat(8));
1075+
out.println("a".repeat(8));
1076+
exp.println("a".repeat(9));
1077+
out.println("a".repeat(9));
1078+
exp.println("a".repeat(10));
1079+
out.println("a".repeat(10));
1080+
exp.println("a".repeat(11));
1081+
out.println("a".repeat(11));
1082+
}
1083+
};
1084+
_servletContextHandler.addServlet(servlet, "/test");
1085+
((HttpConnectionFactory)_connector.getDefaultConnectionFactory()).getHttpConfiguration().setOutputBufferSize(10);
1086+
((HttpConnectionFactory)_connector.getDefaultConnectionFactory()).getHttpConfiguration().setOutputAggregationSize(10);
1087+
_server.start();
1088+
ByteBuffer responseBuf = _connector.getResponse(ByteBuffer.wrap("GET /test HTTP/1.0\nHost: localhost:80\n\n".getBytes(StandardCharsets.UTF_8)));
1089+
String response = BufferUtil.toString(responseBuf, StandardCharsets.UTF_8);
1090+
assertThat(response, containsString("HTTP/1.1 200 OK"));
1091+
String expected = bout.toString(StandardCharsets.UTF_8);
1092+
assertThat(expected, not(emptyString()));
1093+
assertThat(response.replace("\r\n", System.lineSeparator()), containsString(expected));
1094+
}
1095+
10611096
@Test
10621097
public void testReset() throws Exception
10631098
{

0 commit comments

Comments
 (0)
Please sign in to comment.