Author: markt Date: Fri Jun 30 20:29:51 2017 New Revision: 1800454 URL: http://svn.apache.org/viewvc?rev=1800454&view=rev Log: Improve the Default Servlet's handling of static files when the file encoding is not compatible with the required response encoding.
Added: tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt (with props) Modified: tomcat/trunk/build.xml tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java tomcat/trunk/test/org/apache/catalina/servlets/TestDefaultServlet.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/build.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/build.xml?rev=1800454&r1=1800453&r2=1800454&view=diff ============================================================================== --- tomcat/trunk/build.xml (original) +++ tomcat/trunk/build.xml Fri Jun 30 20:29:51 2017 @@ -560,6 +560,7 @@ <exclude name="java/org/apache/**/parser/Token*.java" /> <!-- Exclude simple test files --> <exclude name="test/webapp/bug53257/**/*.txt"/> + <exclude name="test/webapp/bug49nnn/bug49464-ibm850.txt"/> <exclude name="test/webapp-fragments/WEB-INF/classes/*.txt"/> <exclude name="test/webresources/**"/> <!-- Exclude test files with unusual encodings --> Modified: tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java?rev=1800454&r1=1800453&r2=1800454&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java (original) +++ tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java Fri Jun 30 20:29:51 2017 @@ -32,6 +32,8 @@ import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.util.ArrayList; @@ -73,6 +75,7 @@ import org.apache.catalina.connector.Res import org.apache.catalina.util.RequestUtil; import org.apache.catalina.util.ServerInfo; import org.apache.catalina.util.URLEncoder; +import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.security.PrivilegedGetTccl; import org.apache.tomcat.util.security.PrivilegedSetTccl; @@ -230,6 +233,7 @@ public class DefaultServlet extends Http * the platform default is used. */ protected String fileEncoding = null; + private Charset fileEncodingCharset = null; /** * Minimum size for sendfile usage in bytes. @@ -287,6 +291,16 @@ public class DefaultServlet extends Http Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024; fileEncoding = getServletConfig().getInitParameter("fileEncoding"); + if (fileEncoding == null) { + fileEncodingCharset = Charset.defaultCharset(); + fileEncoding = fileEncodingCharset.name(); + } else { + try { + fileEncodingCharset = B2CConverter.getCharset(fileEncoding); + } catch (UnsupportedEncodingException e) { + throw new ServletException(e); + } + } globalXsltFile = getServletConfig().getInitParameter("globalXsltFile"); contextXsltFile = getServletConfig().getInitParameter("contextXsltFile"); @@ -938,7 +952,7 @@ public class DefaultServlet extends Http } } - // Check to see if a Filter, Valve of wrapper has written some content. + // Check to see if a Filter, Valve or wrapper has written some content. // If it has, disable range requests and setting of a content length // since neither can be done reliably. ServletResponse r = response; @@ -1000,19 +1014,38 @@ public class DefaultServlet extends Http renderResult = render(getPathPrefix(request), resource, encoding); } else { // Output is content of resource - if (!checkSendfile(request, response, resource, - contentLength, null)) { - // sendfile not possible so check if resource - // content is available directly + // Check to see if conversion is required + String outputEncoding = response.getCharacterEncoding(); + Charset charset = B2CConverter.getCharset(outputEncoding); + if (charset.equals(fileEncodingCharset)) { + if (!checkSendfile(request, response, resource, + contentLength, null)) { + // sendfile not possible so check if resource + // content is available directly + byte[] resourceBody = resource.getContent(); + if (resourceBody == null) { + // Resource content not available, use + // inputstream + renderResult = resource.getInputStream(); + } else { + // Use the resource content directly + ostream.write(resourceBody); + } + } + } else { + // A conversion is required from fileEncoding to + // response encoding byte[] resourceBody = resource.getContent(); + InputStream source; if (resourceBody == null) { - // Resource content not available, use - // inputstream - renderResult = resource.getInputStream(); + source = resource.getInputStream(); } else { - // Use the resource content directly - ostream.write(resourceBody); + source = new ByteArrayInputStream(resourceBody); } + OutputStreamWriter osw = new OutputStreamWriter(ostream, charset); + PrintWriter pw = new PrintWriter(osw); + copy(resource, source, pw, fileEncoding); + pw.flush(); } } // If a stream was configured, it needs to be copied to Modified: tomcat/trunk/test/org/apache/catalina/servlets/TestDefaultServlet.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/servlets/TestDefaultServlet.java?rev=1800454&r1=1800453&r2=1800454&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/servlets/TestDefaultServlet.java (original) +++ tomcat/trunk/test/org/apache/catalina/servlets/TestDefaultServlet.java Fri Jun 30 20:29:51 2017 @@ -20,7 +20,9 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.io.StringReader; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; @@ -29,6 +31,10 @@ import java.util.Locale; import java.util.Map; import java.util.TimeZone; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import static org.junit.Assert.assertEquals; @@ -46,7 +52,9 @@ import org.apache.catalina.Wrapper; import org.apache.catalina.startup.SimpleHttpClient; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.parser.MediaType; import org.apache.tomcat.websocket.server.WsContextListener; public class TestDefaultServlet extends TomcatBaseTest { @@ -622,4 +630,136 @@ public class TestDefaultServlet extends return true; } } + + @Test + public void testEncodingIncludeStreamOutIso88591() throws Exception { + doTestEncoding(false, "ISO-8859-1"); + } + + @Test + public void testEncodingIncludeWriterOutIso88591() throws Exception { + doTestEncoding(true, "ISO-8859-1"); + } + + @Test + public void testEncodingIncludeStreamOutUtf8() throws Exception { + doTestEncoding(false, "UTF-8"); + } + + @Test + public void testEncodingIncludeWriterOutUtf8() throws Exception { + doTestEncoding(true, "UTF-8"); + } + + @Test + public void testEncodingIncludeStreamOutIbm850() throws Exception { + doTestEncoding(false, "IBM850"); + } + + @Test + public void testEncodingIncludeWriterOutIbm850() throws Exception { + doTestEncoding(false, "IBM850"); + } + + public void doTestEncoding(boolean useWriter, String outputEncoding) throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp"); + + Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); + + Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + defaultServlet.addInitParameter("fileEncoding", "IBM850"); + ctxt.addServletMappingDecoded("/", "default"); + + Tomcat.addServlet(ctxt, "encoding", + new EncodingServlet(outputEncoding, "/bug49nnn/bug49464-ibm850.txt", useWriter)); + ctxt.addServletMappingDecoded("/test", "encoding"); + + tomcat.start(); + + final ByteChunk res = new ByteChunk(); + Map<String,List<String>> headers = new HashMap<>(); + + int rc = getUrl("http://localhost:" + getPort() + "/test", res, headers); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + List<String> values = headers.get("Content-Type"); + if (values.size() == 1) { + MediaType mediaType = MediaType.parseMediaType(new StringReader(values.get(0))); + String charset = mediaType.getCharset(); + if (charset == null) { + res.setCharset(StandardCharsets.ISO_8859_1); + } else { + res.setCharset(B2CConverter.getCharset(charset)); + } + } else { + res.setCharset(StandardCharsets.ISO_8859_1); + } + Assert.assertEquals("½", res.toString()); + } + + @Test + public void testEncodingDirect() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp"); + + Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); + + Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + defaultServlet.addInitParameter("fileEncoding", "IBM850"); + ctxt.addServletMappingDecoded("/", "default"); + + tomcat.start(); + + final ByteChunk res = new ByteChunk(); + Map<String,List<String>> headers = new HashMap<>(); + + int rc = getUrl("http://localhost:" + getPort() + "/bug49nnn/bug49464-ibm850.txt", + res, headers); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + List<String> values = headers.get("Content-Type"); + if (values != null && values.size() == 1) { + MediaType mediaType = MediaType.parseMediaType(new StringReader(values.get(0))); + String charset = mediaType.getCharset(); + if (charset == null) { + res.setCharset(StandardCharsets.ISO_8859_1); + } else { + res.setCharset(B2CConverter.getCharset(charset)); + } + } else { + res.setCharset(StandardCharsets.ISO_8859_1); + } + Assert.assertEquals("½", res.toString()); + } + + private static class EncodingServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + private final String outputEncoding; + private final String includeTarget; + private final boolean useWriter; + + public EncodingServlet(String outputEncoding, String includeTarget, boolean useWriter) { + this.outputEncoding = outputEncoding; + this.includeTarget = includeTarget; + this.useWriter = useWriter; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding(outputEncoding); + if (useWriter) { + resp.getWriter(); + } + resp.flushBuffer(); + RequestDispatcher rd = req.getRequestDispatcher(includeTarget); + rd.include(req, resp); + } + } } Added: tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt URL: http://svn.apache.org/viewvc/tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt?rev=1800454&view=auto ============================================================================== --- tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt (added) +++ tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt [IBM850] Fri Jun 30 20:29:51 2017 @@ -0,0 +1 @@ +½ \ No newline at end of file Propchange: tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/trunk/test/webapp/bug49nnn/bug49464-ibm850.txt ------------------------------------------------------------------------------ svn:mime-type = text/plain; charset="IBM850" Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1800454&r1=1800453&r2=1800454&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Fri Jun 30 20:29:51 2017 @@ -48,6 +48,11 @@ <subsection name="Catalina"> <changelog> <fix> + <bug>49464</bug>: Improve the Default Servlet's handling of static files + when the file encoding is not compatible with the required response + encoding. (markt) + </fix> + <fix> <bug>61214</bug>: Remove deleted attribute <code>servlets</code> from the Context MBean description. Patch provided by Alexis Hassler. (markt) </fix> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org