--- java/org/apache/tomcat/util/net/NioEndpoint.java.orig 2017-03-28 14:39:31.522852483 -0400
+++ java/org/apache/tomcat/util/net/NioEndpoint.java 2017-03-28 14:39:31.528852515 -0400
@@ -1406,11 +1406,15 @@
}
}catch ( IOException x ) {
if ( log.isDebugEnabled() ) log.debug("Unable to complete sendfile request:", x);
- cancelledKey(sk,SocketStatus.ERROR,false);
+ if (!calledByProcessor) {
+ cancelledKey(sk,SocketStatus.ERROR,false);
+ }
return SendfileState.ERROR;
}catch ( Throwable t ) {
log.error("",t);
- cancelledKey(sk, SocketStatus.ERROR, false);
+ if (!calledByProcessor) {
+ cancelledKey(sk, SocketStatus.ERROR, false);
+ }
return SendfileState.ERROR;
}
}
--- webapps/docs/changelog.xml.orig 2017-03-28 14:39:31.523852488 -0400
+++ webapps/docs/changelog.xml 2017-03-28 14:41:38.105546243 -0400
@@ -57,6 +57,16 @@
They eventually become mixed with the numbered issues. (I.e., numbered
issues do not "pop up" wrt. others).
-->
+
+
+
+
+ 60409: When unable to complete sendfile request, ensure the
+ Processor will be added to the cache only once. (markt/violetagg)
+
+
+
+
--- test/org/apache/catalina/connector/TestSendFile.java.orig 2017-03-28 14:39:31.524852493 -0400
+++ test/org/apache/catalina/connector/TestSendFile.java 2017-03-28 14:39:31.527852510 -0400
@@ -22,10 +22,14 @@
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -33,6 +37,8 @@
import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.assertEquals;
+
+import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
@@ -163,4 +169,97 @@
}
}
+
+ @Test
+ public void testBug60409() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ Context ctx = tomcat.addContext("", TEMP_DIR);
+ File file = generateFile(TEMP_DIR, "", EXPECTED_CONTENT_LENGTH);
+ Tomcat.addServlet(ctx, "test", new Bug60409Servlet(file));
+ ctx.addServletMapping("/", "test");
+
+ tomcat.start();
+
+ ByteChunk bc = new ByteChunk();
+ getUrl("http://localhost:" + getPort() + "/test/?" + Globals.SENDFILE_SUPPORTED_ATTR
+ + "=true", bc, null);
+
+ CountDownLatch latch = new CountDownLatch(2);
+ List exceptions = new ArrayList();
+ new Thread(
+ new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, exceptions))
+ .start();
+ new Thread(
+ new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, exceptions))
+ .start();
+
+ latch.await(3000, TimeUnit.MILLISECONDS);
+
+ if (exceptions.size() > 0) {
+ Assert.fail();
+ }
+ }
+
+ private static final class Bug60409Servlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ private final File file;
+
+ Bug60409Servlet(File file) {
+ this.file = file;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ if (Boolean.valueOf(req.getParameter(Globals.SENDFILE_SUPPORTED_ATTR)).booleanValue()) {
+ resp.setContentType("'application/octet-stream");
+ resp.setCharacterEncoding("ISO-8859-1");
+ resp.setContentLength((int) file.length());
+ req.setAttribute(Globals.SENDFILE_FILENAME_ATTR, file.getAbsolutePath());
+ req.setAttribute(Globals.SENDFILE_FILE_START_ATTR, new Long(0));
+ req.setAttribute(Globals.SENDFILE_FILE_END_ATTR, new Long(file.length()));
+ file.delete();
+ } else {
+ byte[] c = new byte[1024];
+ Random rd = new Random();
+ rd.nextBytes(c);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ resp.getOutputStream().write(c);
+ }
+ }
+
+ }
+
+ private static final class RequestExecutor implements Runnable {
+ private final String url;
+ private final CountDownLatch latch;
+ private final List exceptions;
+
+ RequestExecutor(String url, CountDownLatch latch, List exceptions) {
+ this.url = url;
+ this.latch = latch;
+ this.exceptions = exceptions;
+ }
+
+ @Override
+ public void run() {
+ try {
+ ByteChunk result = new ByteChunk();
+ int rc = getUrl(url, result, null);
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ Assert.assertEquals(1024, result.getLength());
+ } catch (Throwable e) {
+ e.printStackTrace();
+ exceptions.add(e);
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ }
}