jetty.project
jetty.project copied to clipboard
jetty-12 ee10 Closure of Response Object
The Servlet Specification 6.0 says:
5.7. Closure of Response Object When a response is closed, the container must immediately flush all remaining content in the response buffer to the client. The following events indicate that the servlet has satisfied the request and that the response object is to be closed: ⢠The termination of the service method of the servlet. ⢠The amount of content specified in the setContentLength or setContentLengthLong method of the response has been greater than zero and has been written to the response. ⢠The sendError method is called. ⢠The sendRedirect method is called. ⢠The complete method on AsyncContext is called.
The tck has a test which sets the contentLength then writes more bytes than that, and also tries to set a header:
int size = 40;
response.setContentLength(size);
PrintWriter pw = response.getWriter();
pw.println("Test PASSED");
StringBuffer tmp = new StringBuffer(2 * size);
int i = 0;
while (i < 8) {
tmp = tmp.append("111111111x");
i = i + 1;
}
pw.println(tmp);
response.addIntHeader("header1", 12345);
Tck test is com/sun/ts/tests/servlet/spec/httpservletresponse/URLClient.java.flushBufferTest
Jetty fails the tck test because we respond with an 500 error instead of ignoring the extra content:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 500 java.io.IOException: written 92 > 40 content-length</title>
</head>
<body>
<h2>HTTP ERROR 500 java.io.IOException: written 92 > 40 content-length</h2>
<table>
<tr><th>URI:</th><td>http://localhost:38225/servlet_spec_httpservletresponse_web/HttpTestServlet?testname=flushBufferTest</td></tr>
<tr><th>STATUS:</th><td>500</td></tr>
<tr><th>MESSAGE:</th><td>java.io.IOException: written 92 > 40 content-length</td></tr>
<tr><th>CAUSED BY:</th><td>java.io.IOException: written 92 > 40 content-length</td></tr>
</table>
<h3>Caused by:</h3><pre>java.io.IOException: written 92 > 40 content-length
at org.eclipse.jetty.server.internal.HttpChannelState$ChannelResponse.write(HttpChannelState.java:1093)
at org.eclipse.jetty.ee10.servlet.HttpOutput.channelWrite(HttpOutput.java:215)
at org.eclipse.jetty.ee10.servlet.HttpOutput.channelWrite(HttpOutput.java:200)
at org.eclipse.jetty.ee10.servlet.HttpOutput.write(HttpOutput.java:804)
at java.base/java.io.ByteArrayOutputStream.writeTo(ByteArrayOutputStream.java:161)
at org.eclipse.jetty.ee10.servlet.writer.Iso88591HttpWriter.write(Iso88591HttpWriter.java:62)
at org.eclipse.jetty.ee10.servlet.writer.HttpWriter.write(HttpWriter.java:69)
at org.eclipse.jetty.ee10.servlet.writer.ResponseWriter.println(ResponseWriter.java:426)
at org.eclipse.jetty.ee10.servlet.writer.ResponseWriter.println(ResponseWriter.java:445)
at com.sun.ts.tests.servlet.spec.httpservletresponse.HttpTestServlet.flushBufferTest(HttpTestServlet.java:59)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at com.sun.ts.tests.servlet.common.servlets.HttpTCKServlet.invokeTest(HttpTCKServlet.java:91)
at com.sun.ts.tests.servlet.common.servlets.HttpTCKServlet.doGet(HttpTCKServlet.java:116)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:527)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at org.eclipse.jetty.ee10.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1383)
at org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:738)
at org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1601)
at org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1534)
at org.eclipse.jetty.ee10.servlet.ServletChannel.lambda$0(ServletChannel.java:425)
at org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:659)
at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:420)
at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:458)
at org.eclipse.jetty.ee10.servlet.security.SecurityHandler.handle(SecurityHandler.java:564)
at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:664)
at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:819)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:149)
at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:665)
at org.eclipse.jetty.server.Handler$Wrapper.handle(Handler.java:611)
at org.eclipse.jetty.server.Server.handle(Server.java:175)
at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:553)
at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:480)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
at java.base/java.lang.Thread.run(Thread.java:833)
</pre>
<hr/><a href="https://eclipse.org/jetty">Powered by Jetty:// 12.0.0-SNAPSHOT</a><hr/>
</body>
</html>
I think this test can be challenged. It is OK that we should close the response if exactly the right amount of data is written, but it is a bit strange to truncate data in excess of the set length. The string written could be multi-byte characters and the truncation could split a character in half.
I think it is reasonable that we throw an exception from such a write rather than truncate.
Still waiting on some action from the servlet spec group, note that this also affects the 6.1 tck and jetty as documented here: https://github.com/jetty/jetty.project/issues/11931