You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bu...@apache.org on 2005/01/14 22:31:50 UTC

DO NOT REPLY [Bug 33106] New: - SSI Processing Enhancements (patch provided)

DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG�
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://issues.apache.org/bugzilla/show_bug.cgi?id=33106>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND�
INSERTED IN THE BUG DATABASE.

http://issues.apache.org/bugzilla/show_bug.cgi?id=33106

           Summary: SSI Processing Enhancements (patch provided)
           Product: Tomcat 5
           Version: 5.5.4
          Platform: PC
        OS/Version: Windows XP
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: Servlets:SSI
        AssignedTo: tomcat-dev@jakarta.apache.org
        ReportedBy: david@eyefinity.com
                CC: david@eyefinity.com


I've made some enhancements to the SSI processing package as follows:

1) Created an SSIFilter to use instead of SSIServlet and modified support
classes accordingly.  SSIFilter takes a contentTypes initParam to filter out
results which shouldn't be processed and removes the buffered initParam since it
isn't relevant.

2) Modified support classes to allow SSIFilter and SSIServlet to update the
Last-Modified header with dates from processing directives and include files. 
(This is better than using the Expires header, which is still supported.)

3) Moved Globals.SSI_FLAG_ATTR into SSIProcessor to make it easier to use the
ssi package in other web-servers that don't support SSI processing.  Now there
are only three external dependencies (IOTools, Strftime, URLEncoder) which I
recommend be included in servlets-ssi.jar so someone can just grab that jar and
be done with it.  You may want to consider removing the SSI_FLAG_ATTR from
Globals since it doesn't seem to be used anywhere else.

Patches (diff -u) Follow:
--- ./ResponseIncludeWrapper.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/ResponseIncludeWrapper.java	Fri Jan 14 10:34:00 2005
@@ -13,6 +13,10 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponseWrapper;
@@ -24,12 +28,17 @@
  * @version $Revision: 1.5 $, $Date: 2004/09/01 18:33:33 $
  */
 public class ResponseIncludeWrapper extends HttpServletResponseWrapper {
+    private static final String CONTENT_TYPE = "content-type";
+    private static final String LAST_MODIFIED = "last-modified";
+    private DateFormat lmParser = new SimpleDateFormat("EEE, d MMM yyyy
HH:mm:ss z");
     /**
      * Our ServletOutputStream
      */
     protected ServletOutputStream originalServletOutputStream;
     protected ServletOutputStream servletOutputStream;
     protected PrintWriter printWriter;
+    protected long lastModified = 0;
+    private String contentType = null;
 
 
     /**
@@ -98,5 +107,85 @@
             return servletOutputStream;
         }
         throw new IllegalStateException();
+    }
+
+
+    /**
+     * Returns the value of the <code>last-modified</code> header field. The
result is
+     * the number of milliseconds since January 1, 1970 GMT.
+     * 
+     * @return the date the resource referenced by this
<code>ResponseIncludeWrapper</code> was last modified, or 0 if not known.
+     */
+    public long getLastModified() {
+        return lastModified;
+    }
+    
+    
+    /**
+     * Returns the value of the <code>content-type</code> header field.
+     * 
+     * @return the content type of the resource referenced by this
<code>ResponseIncludeWrapper</code>, or <code>null</code> if not known.
+     */
+    public String getContentType()
+    {
+        return contentType;
+    }
+
+    
+    public void addDateHeader(String name, long value) {
+        super.addDateHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            lastModified = value;
+        }
+    }
+
+    
+    public void addHeader(String name, String value) {
+        super.addHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            try {
+                lastModified = lmParser.parse(value).getTime();
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        else if (lname.equals(CONTENT_TYPE))
+        {
+            contentType = value;
+        }
+    }
+
+    
+    public void setDateHeader(String name, long value) {
+        super.setDateHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            lastModified = value;
+        }
+    }
+ 
+    
+    public void setHeader(String name, String value) {
+        super.setHeader(name, value);
+        String lname = name.toLowerCase();
+        if (lname.equals(LAST_MODIFIED)) {
+            try {
+                lastModified = lmParser.parse(value).getTime();
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        else if (lname.equals(CONTENT_TYPE))
+        {
+            contentType = value;
+        }
+    }
+
+    public void setContentType(String value)
+    {
+        super.setContentType(value);
+        contentType = value;
     }
 }
--- ./SSICommand.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSICommand.java	Fri Jan 14 11:37:00 2005
@@ -34,10 +34,11 @@
      *            The parameter values
      * @param writer
      *            the writer to output to
+     * @return the most current modified date resulting from any SSI commands
      * @throws SSIStopProcessingException
      *             if SSI processing should be aborted
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer)
             throws SSIStopProcessingException;
 }
--- ./SSIConditional.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIConditional.java	Fri Jan 14 11:47:00 2005
@@ -23,9 +23,10 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer)
             throws SSIStopProcessingException {
+        long lastModified = System.currentTimeMillis();
         // Retrieve the current state information
         SSIConditionalState state = ssiMediator.getConditionalState();
         if ("if".equalsIgnoreCase(commandName)) {
@@ -33,7 +34,7 @@
             // except count it
             if (state.processConditionalCommandsOnly) {
                 state.nestingCount++;
-                return;
+                return lastModified;
             }
             state.nestingCount = 0;
             // Evaluate the expression
@@ -48,12 +49,12 @@
         } else if ("elif".equalsIgnoreCase(commandName)) {
             // No need to even execute if we are nested in
             // a false branch
-            if (state.nestingCount > 0) return;
+            if (state.nestingCount > 0) return lastModified;
             // If a branch was already taken in this if block
             // then disable output and return
             if (state.branchTaken) {
                 state.processConditionalCommandsOnly = true;
-                return;
+                return lastModified;
             }
             // Evaluate the expression
             if (evaluateArguments(paramNames, paramValues, ssiMediator)) {
@@ -68,7 +69,7 @@
         } else if ("else".equalsIgnoreCase(commandName)) {
             // No need to even execute if we are nested in
             // a false branch
-            if (state.nestingCount > 0) return;
+            if (state.nestingCount > 0) return lastModified;
             // If we've already taken another branch then
             // disable output otherwise enable it.
             state.processConditionalCommandsOnly = state.branchTaken;
@@ -80,7 +81,7 @@
             // one level on the nesting count
             if (state.nestingCount > 0) {
                 state.nestingCount--;
-                return;
+                return lastModified;
             }
             // Turn output back on
             state.processConditionalCommandsOnly = false;
@@ -93,6 +94,7 @@
             //throw new SsiCommandException( "Not a conditional command:" +
             // cmdName );
         }
+        return lastModified;
     }
 
 
--- ./SSIConfig.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIConfig.java	Fri Jan 14 11:48:00 2005
@@ -24,8 +24,9 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         for (int i = 0; i < paramNames.length; i++) {
             String paramName = paramNames[i];
             String paramValue = paramValues[i];
@@ -33,10 +34,13 @@
                     .substituteVariables(paramValue);
             if (paramName.equalsIgnoreCase("errmsg")) {
                 ssiMediator.setConfigErrMsg(substitutedValue);
+                lastModified = System.currentTimeMillis();
             } else if (paramName.equalsIgnoreCase("sizefmt")) {
                 ssiMediator.setConfigSizeFmt(substitutedValue);
+                lastModified = System.currentTimeMillis();
             } else if (paramName.equalsIgnoreCase("timefmt")) {
                 ssiMediator.setConfigTimeFmt(substitutedValue);
+                lastModified = System.currentTimeMillis();
             } else {
                 ssiMediator.log("#config--Invalid attribute: " + paramName);
                 //We need to fetch this value each time, since it may change
@@ -46,5 +50,6 @@
                 writer.write(configErrMsg);
             }
         }
+        return lastModified;
     }
 }
--- ./SSIEcho.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIEcho.java	Fri Jan 14 11:49:00 2005
@@ -28,8 +28,9 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         String encoding = DEFAULT_ENCODING;
         String errorMessage = ssiMediator.getConfigErrMsg();
         for (int i = 0; i < paramNames.length; i++) {
@@ -42,6 +43,7 @@
                     variableValue = MISSING_VARIABLE_VALUE;
                 }
                 writer.write(variableValue);
+                lastModified = System.currentTimeMillis();
             } else if (paramName.equalsIgnoreCase("encoding")) {
                 if (isValidEncoding(paramValue)) {
                     encoding = paramValue;
@@ -54,6 +56,7 @@
                 writer.write(errorMessage);
             }
         }
+        return lastModified;
     }
 
 
--- ./SSIExec.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIExec.java	Fri Jan 14 11:51:00 2005
@@ -15,6 +15,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+
 import org.apache.catalina.util.IOTools;
 /**
  * Implements the Server-side #exec command
@@ -33,16 +34,17 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         String configErrMsg = ssiMediator.getConfigErrMsg();
         String paramName = paramNames[0];
         String paramValue = paramValues[0];
         String substitutedValue = ssiMediator.substituteVariables(paramValue);
         if (paramName.equalsIgnoreCase("cgi")) {
-            ssiInclude.process(ssiMediator, "include",
-                    new String[]{"virtual"}, new String[]{substitutedValue},
-                    writer);
+            lastModified = ssiInclude.process(ssiMediator, "include",
+                               new String[]{"virtual"}, new
String[]{substitutedValue},
+                               writer);
         } else if (paramName.equalsIgnoreCase("cmd")) {
             boolean foundProgram = false;
             try {
@@ -57,6 +59,7 @@
                 IOTools.flow(stdErrReader, writer, buf);
                 IOTools.flow(stdOutReader, writer, buf);
                 proc.waitFor();
+                lastModified = System.currentTimeMillis();
             } catch (InterruptedException e) {
                 ssiMediator.log("Couldn't exec file: " + substitutedValue, e);
                 writer.write(configErrMsg);
@@ -68,5 +71,6 @@
                 ssiMediator.log("Couldn't exec file: " + substitutedValue, e);
             }
         }
+        return lastModified;
     }
 }
--- ./SSIFlastmod.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIFlastmod.java	Fri Jan 14 11:43:00 2005
@@ -28,8 +28,9 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         String configErrMsg = ssiMediator.getConfigErrMsg();
         StringBuffer buf = new StringBuffer();
         for (int i = 0; i < paramNames.length; i++) {
@@ -41,7 +42,7 @@
                 if (paramName.equalsIgnoreCase("file")
                         || paramName.equalsIgnoreCase("virtual")) {
                     boolean virtual = paramName.equalsIgnoreCase("virtual");
-                    long lastModified = ssiMediator.getFileLastModified(
+                    lastModified = ssiMediator.getFileLastModified(
                             substitutedValue, virtual);
                     Date date = new Date(lastModified);
                     String configTimeFmt = ssiMediator.getConfigTimeFmt();
@@ -58,6 +59,7 @@
                 writer.write(configErrMsg);
             }
         }
+        return lastModified;
     }
 
 
--- ./SSIFsize.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIFsize.java	Fri Jan 14 11:44:00 2005
@@ -30,8 +30,9 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         String configErrMsg = ssiMediator.getConfigErrMsg();
         for (int i = 0; i < paramNames.length; i++) {
             String paramName = paramNames[i];
@@ -42,6 +43,8 @@
                 if (paramName.equalsIgnoreCase("file")
                         || paramName.equalsIgnoreCase("virtual")) {
                     boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                        substitutedValue, virtual);
                     long size = ssiMediator.getFileSize(substitutedValue,
                             virtual);
                     String configSizeFmt = ssiMediator.getConfigSizeFmt();
@@ -56,6 +59,7 @@
                 writer.write(configErrMsg);
             }
         }
+        return lastModified;
     }
 
 
--- ./SSIInclude.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIInclude.java	Fri Jan 14 11:53:00 2005
@@ -25,8 +25,9 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         String configErrMsg = ssiMediator.getConfigErrMsg();
         for (int i = 0; i < paramNames.length; i++) {
             String paramName = paramNames[i];
@@ -37,6 +38,8 @@
                 if (paramName.equalsIgnoreCase("file")
                         || paramName.equalsIgnoreCase("virtual")) {
                     boolean virtual = paramName.equalsIgnoreCase("virtual");
+                    lastModified = ssiMediator.getFileLastModified(
+                        substitutedValue, virtual);
                     String text = ssiMediator.getFileText(substitutedValue,
                             virtual);
                     writer.write(text);
@@ -51,5 +54,6 @@
                 writer.write(configErrMsg);
             }
         }
+        return lastModified;
     }
 }
--- ./SSIMediator.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIMediator.java	Fri Jan 14 11:26:00 2005
@@ -41,7 +41,7 @@
     protected String configSizeFmt = DEFAULT_CONFIG_SIZE_FMT;
     protected String className = getClass().getName();
     protected SSIExternalResolver ssiExternalResolver;
-    protected Date lastModifiedDate;
+    protected long lastModifiedDate;
     protected int debug;
     protected Strftime strftime;
     protected SSIConditionalState conditionalState = new SSIConditionalState();
@@ -64,7 +64,7 @@
 
 
     public SSIMediator(SSIExternalResolver ssiExternalResolver,
-            Date lastModifiedDate, int debug) {
+            long lastModifiedDate, int debug) {
         this.ssiExternalResolver = ssiExternalResolver;
         this.lastModifiedDate = lastModifiedDate;
         this.debug = debug;
@@ -315,7 +315,7 @@
             setVariableValue("DATE_LOCAL", null);
             ssiExternalResolver.setVariableValue(className + ".DATE_LOCAL",
                     retVal);
-            retVal = formatDate(lastModifiedDate, null);
+            retVal = formatDate(new Date(lastModifiedDate), null);
             setVariableValue("LAST_MODIFIED", null);
             ssiExternalResolver.setVariableValue(className + ".LAST_MODIFIED",
                     retVal);
--- ./SSIPrintenv.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIPrintenv.java	Fri Jan 14 11:46:00 2005
@@ -24,8 +24,9 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer) {
+        long lastModified = 0;
         //any arguments should produce an error
         if (paramNames.length > 0) {
             String errorMessage = ssiMediator.getConfigErrMsg();
@@ -46,7 +47,9 @@
                 writer.write('=');
                 writer.write(variableValue);
                 writer.write('\n');
+                lastModified = System.currentTimeMillis();
             }
         }
+        return lastModified;
     }
 }
--- ./SSIProcessor.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIProcessor.java	Fri Jan 14 11:37:00 2005
@@ -15,9 +15,9 @@
 import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.StringWriter;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.StringTokenizer;
+
 import org.apache.catalina.util.IOTools;
 /**
  * The entry point to SSI processing. This class does the actual parsing,
@@ -36,7 +36,16 @@
     protected SSIExternalResolver ssiExternalResolver;
     protected HashMap commands = new HashMap();
     protected int debug;
-
+    
+    /**
+     * The servlet context attribute under which we store a flag used
+     * to mark this request as having been processed by the SSIServlet.
+     * We do this because of the pathInfo mangling happening when using
+     * the CGIServlet in conjunction with the SSI servlet. (value stored
+     * as an object of type String)
+     */
+     public static final String SSI_FLAG_ATTR =
+         "org.apache.catalina.ssi.SSIServlet";
 
     public SSIProcessor(SSIExternalResolver ssiExternalResolver, int debug) {
         this.ssiExternalResolver = ssiExternalResolver;
@@ -76,11 +85,12 @@
      *            the reader to read the file containing SSIs from
      * @param writer
      *            the writer to write the file with the SSIs processed.
+     * @return the most current modified date resulting from any SSI commands
      * @throws IOException
      *             when things go horribly awry. Should be unlikely since the
      *             SSICommand usually catches 'normal' IOExceptions.
      */
-    public void process(Reader reader, Date lastModifiedDate,
+    public long process(Reader reader, long lastModifiedDate,
             PrintWriter writer) throws IOException {
         SSIMediator ssiMediator = new SSIMediator(ssiExternalResolver,
                 lastModifiedDate, debug);
@@ -142,8 +152,11 @@
                             // command is not conditional
                             if
(!ssiMediator.getConditionalState().processConditionalCommandsOnly
                                     || ssiCommand instanceof SSIConditional) {
-                                ssiCommand.process(ssiMediator, strCmd,
-                                        paramNames, paramValues, writer);
+                                long lmd = ssiCommand.process(ssiMediator, strCmd,
+                                               paramNames, paramValues, writer);
+                                if (lmd > lastModifiedDate) {
+                                    lastModifiedDate = lmd;
+                                }                                    
                             }
                         }
                         if (errorMessage != null) {
@@ -160,6 +173,7 @@
             //If we are here, then we have already stopped processing, so all
             // is good
         }
+        return lastModifiedDate;
     }
 
 
--- ./SSIServlet.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIServlet.java	Fri Jan 14 11:34:00 2005
@@ -19,13 +19,12 @@
 import java.io.StringWriter;
 import java.net.URL;
 import java.net.URLConnection;
-import java.util.Date;
+
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.catalina.Globals;
 /**
  * Servlet to process SSI requests within a webpage. Mapped to a path from
  * within web.xml.
@@ -166,7 +165,7 @@
             res.setDateHeader("Expires", (new java.util.Date()).getTime()
                     + expires.longValue() * 1000);
         }
-        req.setAttribute(Globals.SSI_FLAG_ATTR, "true");
+        req.setAttribute(SSIProcessor.SSI_FLAG_ATTR, "true");
         processSSI(req, res, resource);
     }
 
@@ -174,7 +173,7 @@
     protected void processSSI(HttpServletRequest req, HttpServletResponse res,
             URL resource) throws IOException {
         SSIExternalResolver ssiExternalResolver = new SSIServletExternalResolver(
-                this, req, res, isVirtualWebappRelative, debug);
+                getServletContext(), req, res, isVirtualWebappRelative, debug);
         SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver,
                 debug);
         PrintWriter printWriter = null;
@@ -189,8 +188,10 @@
         InputStream resourceInputStream = resourceInfo.getInputStream();
         BufferedReader bufferedReader = new BufferedReader(
                 new InputStreamReader(resourceInputStream));
-        Date lastModifiedDate = new Date(resourceInfo.getLastModified());
-        ssiProcessor.process(bufferedReader, lastModifiedDate, printWriter);
+        long lastModified = ssiProcessor.process(bufferedReader,
resourceInfo.getLastModified(), printWriter);
+        if (lastModified > 0) {
+            res.setDateHeader("Last-Modified", lastModified);
+        }
         if (buffered) {
             printWriter.flush();
             String text = stringWriter.toString();
--- ./SSIServletExternalResolver.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSIServletExternalResolver.java	Fri Jan 14 09:31:00 2005
@@ -12,16 +12,17 @@
 
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLDecoder;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Enumeration;
+
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 /**
@@ -37,17 +38,17 @@
             "QUERY_STRING", "QUERY_STRING_UNESCAPED", "REMOTE_ADDR",
             "REMOTE_HOST", "REMOTE_USER", "REQUEST_METHOD", "SCRIPT_NAME",
             "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE"};
-    protected HttpServlet servlet;
+    protected ServletContext context;
     protected HttpServletRequest req;
     protected HttpServletResponse res;
     protected boolean isVirtualWebappRelative;
     protected int debug;
 
 
-    public SSIServletExternalResolver(HttpServlet servlet,
+    public SSIServletExternalResolver(ServletContext context,
             HttpServletRequest req, HttpServletResponse res,
             boolean isVirtualWebappRelative, int debug) {
-        this.servlet = servlet;
+        this.context = context;
         this.req = req;
         this.res = res;
         this.isVirtualWebappRelative = isVirtualWebappRelative;
@@ -60,9 +61,9 @@
         //is the same as Servlet.log( message ), since API
         //doesn't seem to say so.
         if (throwable != null) {
-            servlet.log(message, throwable);
+            context.log(message, throwable);
         } else {
-            servlet.log(message);
+            context.log(message);
         }
     }
 
@@ -160,7 +161,14 @@
         } else if (name.equalsIgnoreCase("QUERY_STRING_UNESCAPED")) {
             String queryString = req.getQueryString();
             if (queryString != null) {
-                retVal = URLDecoder.decode(queryString);
+                try
+                {
+                    retVal = URLDecoder.decode(queryString, "UTF-8");
+                }
+                catch (UnsupportedEncodingException e)
+                {
+                    retVal = queryString;
+                }
             }
         } else if (name.equalsIgnoreCase("REMOTE_ADDR")) {
             retVal = req.getRemoteAddr();
@@ -179,8 +187,7 @@
         } else if (name.equalsIgnoreCase("SERVER_PROTOCOL")) {
             retVal = req.getProtocol();
         } else if (name.equalsIgnoreCase("SERVER_SOFTWARE")) {
-            ServletContext servletContext = servlet.getServletContext();
-            retVal = servletContext.getServerInfo();
+            retVal = context.getServerInfo();
         }
         return retVal;
     }
@@ -250,26 +257,23 @@
                     + nonVirtualPath);
         }
         String path = getAbsolutePath(nonVirtualPath);
-        ServletContext servletContext = servlet.getServletContext();
         ServletContextAndPath csAndP = new ServletContextAndPath(
-                servletContext, path);
+                context, path);
         return csAndP;
     }
 
 
     protected ServletContextAndPath getServletContextAndPathFromVirtualPath(
             String virtualPath) throws IOException {
-        ServletContext servletContext = servlet.getServletContext();
-        String path = null;
         if (!virtualPath.startsWith("/") && !virtualPath.startsWith("\\")) {
-            path = getAbsolutePath(virtualPath);
+            return new ServletContextAndPath(context,
getAbsolutePath(virtualPath));
         } else {
             String normalized = SSIServletRequestUtil.normalize(virtualPath);
             if (isVirtualWebappRelative) {
-                path = normalized;
+                return new ServletContextAndPath(context, normalized);
             } else {
-                servletContext = servletContext.getContext(normalized);
-                if (servletContext == null) {
+                ServletContext normContext = context.getContext(normalized);
+                if (normContext == null) {
                     throw new IOException("Couldn't get context for path: "
                             + normalized);
                 }
@@ -277,19 +281,19 @@
                 // to remove,
                 // ie:
                 // '/file1.shtml' vs '/appName1/file1.shtml'
-                if (!isRootContext(servletContext)) {
-                    path = getPathWithoutContext(normalized);
-                    if (path == null) {
+                if (!isRootContext(normContext)) {
+                    String noContext = getPathWithoutContext(normalized);
+                    if (noContext == null) {
                         throw new IOException(
                                 "Couldn't remove context from path: "
                                         + normalized);
                     }
+                    return new ServletContextAndPath(normContext, noContext);
                 } else {
-                    path = normalized;
+                    return new ServletContextAndPath(normContext, normalized);
                 }
             }
         }
-        return new ServletContextAndPath(servletContext, path);
     }
 
 
--- ./SSISet.java	Fri Oct 29 16:13:00 2004
+++ ../ssi_new/SSISet.java	Fri Jan 14 11:52:00 2005
@@ -23,9 +23,10 @@
     /**
      * @see SSICommand
      */
-    public void process(SSIMediator ssiMediator, String commandName,
+    public long process(SSIMediator ssiMediator, String commandName,
             String[] paramNames, String[] paramValues, PrintWriter writer)
             throws SSIStopProcessingException {
+        long lastModified = 0;
         String errorMessage = ssiMediator.getConfigErrMsg();
         String variableName = null;
         for (int i = 0; i < paramNames.length; i++) {
@@ -39,6 +40,7 @@
                             .substituteVariables(paramValue);
                     ssiMediator.setVariableValue(variableName,
                             substitutedValue);
+                    lastModified = System.currentTimeMillis();
                 } else {
                     ssiMediator.log("#set--no variable specified");
                     writer.write(errorMessage);
@@ -50,5 +52,6 @@
                 throw new SSIStopProcessingException();
             }
         }
+        return lastModified;
     }
 }
--- SSIFilter.java	Fri Jan 14 13:25:44 2005
+++ ../ssi_new/SSIFilter.java	Fri Jan 14 13:30:06 2005
@@ -0,0 +1,189 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License
+ * at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
+ * law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+/**
+ * Filter to process SSI requests within a webpage. Mapped to a content types from
+ * within web.xml.
+ *
+ * Based on code from <code>org.apache.catalina.ssi.SSIServlet</code>.
+ * 
+ * @author David Becker
+ * @version $Revision: 1.0 $, $Date: 2005/01/14 13:28:00 $
+ * @see org.apache.catalina.ssi.SSIServlet
+ */
+public class SSIFilter implements Filter {
+    /** Configuration for this filter. */
+    protected FilterConfig config = null;
+    /** Debug level for this filter. */
+    protected int debug = 0;
+    /** Expiration time in seconds for the doc. */
+    protected Long expires = null;
+    /** virtual path can be webapp-relative */
+    protected boolean isVirtualWebappRelative = false;
+    /** content types allowed for SSI processing */
+    protected List contentTypes = new ArrayList();
+    /** should all content types be allowed */
+    protected boolean allowAllContentTypes = false;
+
+
+    //----------------- Public methods.
+    /**
+     * Initialize this filter.
+     * 
+     * @exception ServletException
+     *                if an error occurs
+     */
+    public void init(FilterConfig config) throws ServletException {
+        this.config = config;
+        String value = null;
+        try {
+            value = config.getInitParameter("debug");
+            debug = Integer.parseInt(value);
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = config.getInitParameter(
+                    "isVirtualWebappRelative");
+            isVirtualWebappRelative = Integer.parseInt(value) > 0?true:false;
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            value = config.getInitParameter("expires");
+            expires = Long.valueOf(value);
+        } catch (NumberFormatException e) {
+            expires = null;
+            config.getServletContext().log("Invalid format for expires
initParam; expected integer (seconds)");
+        } catch (Throwable t) {
+            ;
+        }
+        try {
+            String types = config.getInitParameter("contentTypes");
+            allowAllContentTypes = (types.equals("*") || types.equals("*/*"));
+            if ((types != null) && !types.equals(""))
+            {
+                contentTypes = Arrays.asList(types.split(","));
+            }
+            else
+            {
+                contentTypes.add("text/html");
+                config.getServletContext().log("No contentTypes initParam
provided; defaulting to text/html");
+            }
+        } catch (Throwable t) {
+            ;
+        }
+        if (debug > 0)
+            config.getServletContext().log("SSIFilter.init() SSI invoker
started with 'debug'=" + debug);
+    }
+
+
+    public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
+        // cast once
+        HttpServletRequest req = (HttpServletRequest)request;
+        HttpServletResponse res = (HttpServletResponse)response;
+        
+        // indicate that we're in SSI processing
+        req.setAttribute(SSIProcessor.SSI_FLAG_ATTR, "true");           
+
+        // setup to capture output
+        ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream();
+        ResponseIncludeWrapper responseIncludeWrapper = new
ResponseIncludeWrapper(res, basos);
+
+        // process remainder of filter chain
+        chain.doFilter(req, responseIncludeWrapper);
+
+        // we can't assume the chain flushed its output
+        responseIncludeWrapper.flushOutputStreamOrWriter();
+        byte[] bytes = basos.toByteArray();
+
+        // get content type
+        String contentType = responseIncludeWrapper.getContentType();
+        if ((contentType == null) || contentType.equals("")) {
+            contentType =
config.getServletContext().getMimeType(req.getRequestURI());
+            if ((contentType == null) || contentType.equals("")) {
+                contentType = "text/html";
+            }
+        }
+        if (contentType.indexOf(";") > -1)
+        {
+            contentType = contentType.substring(0, contentType.indexOf(";"));
+        }
+
+        // is this an allowed type for SSI processing?
+        if (allowAllContentTypes || contentTypes.contains(contentType)) {
+
+            // set up SSI processing 
+            SSIExternalResolver ssiExternalResolver = new
SSIServletExternalResolver(
+                    config.getServletContext(), req, res,
isVirtualWebappRelative, debug);
+            SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver,
+                    debug);
+            
+            // prepare readers/writers
+            Reader reader = new InputStreamReader(new ByteArrayInputStream(bytes));
+            ByteArrayOutputStream ssiout = new ByteArrayOutputStream();
+            PrintWriter writer = new PrintWriter(new OutputStreamWriter(ssiout));
+            
+            // do SSI processing  
+            long lastModified = ssiProcessor.process(reader,
responseIncludeWrapper.getLastModified(), writer);
+            
+            // set output bytes
+            writer.flush();
+            bytes = ssiout.toByteArray();
+            
+            // override headers
+            if (expires != null) {
+                res.setDateHeader("Expires", (new java.util.Date()).getTime()
+                        + expires.longValue() * 1000);
+            }
+            if (lastModified > 0) {
+                res.setDateHeader("Last-Modified", lastModified);
+            }
+        }
+
+        // write output
+        try {
+            OutputStream out = res.getOutputStream();
+            out.write(bytes);
+        } catch (Throwable t) {
+            Writer out = res.getWriter();
+            out.write(new String(bytes));
+        }
+    }
+
+
+    public void destroy()
+    {
+    }
+}

-- 
Configure bugmail: http://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org