tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cos...@apache.org
Subject svn commit: r534293 [4/11] - in /tomcat/sandbox/tomcat-lite: ./ bin/ external/ java/ java/org/apache/commons/logging/ java/org/apache/tomcat/lite/ java/org/apache/tomcat/lite/ctxmap/ java/org/apache/tomcat/lite/http/ java/org/apache/tomcat/lite/http11/...
Date Wed, 02 May 2007 02:22:50 GMT
Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java?view=auto&rev=534293
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/WebappServletMapper.java Tue May  1 19:22:45 2007
@@ -0,0 +1,856 @@
+/*
+ *  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.tomcat.lite;
+
+import java.io.File;
+
+import org.apache.tomcat.lite.util.MappingData;
+import org.apache.tomcat.util.buf.Ascii;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * Mapper, which implements the servlet API mapping rules (which are derived
+ * from the HTTP rules).
+ * 
+ * Based on catalina mapper - but simplified. All host and context mappings
+ * is done in HostMapper - this is just dealing with web.xml.
+ * 
+ * For corner cases ( very large number of rules, dynamic rules, etc ) you 
+ * can override the mapper for a context with a class extending this.
+ */
+public class WebappServletMapper {
+
+    /**
+     * Context associated with this wrapper, used for wrapper mapping.
+     */
+    public ContextMapElement contextMapElement = new ContextMapElement();
+
+
+    // --------------------------------------------------------- Public Methods
+
+    public WebappServletMapper(ServletContextImpl impl) {
+        contextMapElement.object = impl;
+        contextMapElement.name = impl.getContextPath();
+    }
+
+
+   /** Set context, used for wrapper mapping (request dispatcher).
+     *
+     * @param welcomeResources Welcome files defined for this context
+     * @param resources Static resources of the context
+     */
+    public void setContext(String path, String[] welcomeResources,
+                           File resources) {
+        contextMapElement.name = path;
+        contextMapElement.welcomeResources = welcomeResources;
+        contextMapElement.resources = resources;
+    }
+
+
+    /**
+     * Add a wrapper to the context associated with this wrapper.
+     *
+     * @param path Wrapper mapping
+     * @param wrapper The Wrapper object
+     */
+    public void addWrapper(String path, Object wrapper) {
+        addWrapper(contextMapElement, path, wrapper);
+    }
+
+
+    public void addWrapper(String path, Object wrapper, boolean jspWildCard) {
+        addWrapper(contextMapElement, path, wrapper, jspWildCard);
+    }
+
+
+    public void addWrapper(ContextMapElement context, String path, Object wrapper) {
+        addWrapper(context, path, wrapper, false);
+    }
+
+
+    /**
+     * Adds a wrapper to the given context.
+     *
+     * @param context The context to which to add the wrapper
+     * @param path Wrapper mapping
+     * @param wrapper The Wrapper object
+     * @param jspWildCard true if the wrapper corresponds to the JspServlet
+     * and the mapping path contains a wildcard; false otherwise
+     */
+    protected void addWrapper(ContextMapElement context, String path, Object wrapper,
+                              boolean jspWildCard) {
+
+        synchronized (context) {
+            WrapperMapElement newWrapper = new WrapperMapElement();
+            newWrapper.object = wrapper;
+            newWrapper.jspWildCard = jspWildCard;
+            if (path.endsWith("/*")) {
+                // Wildcard wrapper
+                newWrapper.name = path.substring(0, path.length() - 2);
+                WrapperMapElement[] oldWrappers = context.wildcardWrappers;
+                WrapperMapElement[] newWrappers =
+                    new WrapperMapElement[oldWrappers.length + 1];
+                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
+                    context.wildcardWrappers = newWrappers;
+                    int slashCount = slashCount(newWrapper.name);
+                    if (slashCount > context.nesting) {
+                        context.nesting = slashCount;
+                    }
+                }
+            } else if (path.startsWith("*.")) {
+                // Extension wrapper
+                newWrapper.name = path.substring(2);
+                WrapperMapElement[] oldWrappers = context.extensionWrappers;
+                WrapperMapElement[] newWrappers =
+                    new WrapperMapElement[oldWrappers.length + 1];
+                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
+                    context.extensionWrappers = newWrappers;
+                }
+            } else if (path.equals("/")) {
+                // Default wrapper
+                newWrapper.name = "";
+                context.defaultWrapper = newWrapper;
+            } else {
+                // Exact wrapper
+                newWrapper.name = path;
+                WrapperMapElement[] oldWrappers = context.exactWrappers;
+                WrapperMapElement[] newWrappers =
+                    new WrapperMapElement[oldWrappers.length + 1];
+                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
+                    context.exactWrappers = newWrappers;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Remove a wrapper from the context associated with this wrapper.
+     *
+     * @param path Wrapper mapping
+     */
+    public void removeWrapper(String path) {
+        removeWrapper(contextMapElement, path);
+    }
+
+
+    protected void removeWrapper(ContextMapElement context, String path) {
+        synchronized (context) {
+            if (path.endsWith("/*")) {
+                // Wildcard wrapper
+                String name = path.substring(0, path.length() - 2);
+                WrapperMapElement[] oldWrappers = context.wildcardWrappers;
+                WrapperMapElement[] newWrappers =
+                    new WrapperMapElement[oldWrappers.length - 1];
+                if (removeMap(oldWrappers, newWrappers, name)) {
+                    // Recalculate nesting
+                    context.nesting = 0;
+                    for (int i = 0; i < newWrappers.length; i++) {
+                        int slashCount = slashCount(newWrappers[i].name);
+                        if (slashCount > context.nesting) {
+                            context.nesting = slashCount;
+                        }
+                    }
+                    context.wildcardWrappers = newWrappers;
+                }
+            } else if (path.startsWith("*.")) {
+                // Extension wrapper
+                String name = path.substring(2);
+                WrapperMapElement[] oldWrappers = context.extensionWrappers;
+                WrapperMapElement[] newWrappers =
+                    new WrapperMapElement[oldWrappers.length - 1];
+                if (removeMap(oldWrappers, newWrappers, name)) {
+                    context.extensionWrappers = newWrappers;
+                }
+            } else if (path.equals("/")) {
+                // Default wrapper
+                context.defaultWrapper = null;
+            } else {
+                // Exact wrapper
+                String name = path;
+                WrapperMapElement[] oldWrappers = context.exactWrappers;
+                WrapperMapElement[] newWrappers =
+                    new WrapperMapElement[oldWrappers.length - 1];
+                if (removeMap(oldWrappers, newWrappers, name)) {
+                    context.exactWrappers = newWrappers;
+                }
+            }
+        }
+    }
+
+    /**
+     * Map the specified URI relative to the context,
+     * mutating the given mapping data.
+     *
+     * @param uri URI
+     * @param mappingData This structure will contain the result of the mapping
+     *                    operation
+     */
+    public void map(MessageBytes uri, MappingData mappingData)
+        throws Exception {
+
+        uri.toChars();
+        CharChunk uricc = uri.getCharChunk();
+        //uricc.setLimit(-1);
+        internalMapWrapper(contextMapElement, uricc, mappingData);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Wrapper mapping.
+     */
+    private final void internalMapWrapper(ContextMapElement context,
+                                          CharChunk path,
+                                          MappingData mappingData)
+        throws Exception {
+
+        int pathOffset = path.getOffset();
+        int pathEnd = path.getEnd();
+        int servletPath = pathOffset;
+        boolean noServletPath = false;
+
+        int length = context.name.length();
+        if (length == 1) length--;
+        if (length != (pathEnd - pathOffset)) {
+            servletPath = pathOffset + length;
+        } else {
+            noServletPath = true;
+            // What is this doing ??? 
+            path.append('/');
+            pathOffset = path.getOffset();
+            pathEnd = path.getEnd();
+            servletPath = pathOffset+length;
+        }
+
+        path.setOffset(servletPath);
+
+        // Rule 1 -- Exact Match
+        WrapperMapElement[] exactWrappers = context.exactWrappers;
+        internalMapExactWrapper(exactWrappers, path, mappingData);
+
+        // Rule 2 -- Prefix Match
+        boolean checkJspWelcomeFiles = false;
+        WrapperMapElement[] wildcardWrappers = context.wildcardWrappers;
+        if (mappingData.wrapper == null) {
+            internalMapWildcardWrapper(wildcardWrappers, context.nesting, 
+                                       path, mappingData);
+            if (mappingData.wrapper != null && mappingData.jspWildCard) {
+                char[] buf = path.getBuffer();
+                if (buf[pathEnd - 1] == '/') {
+                    /*
+                     * Path ending in '/' was mapped to JSP servlet based on
+                     * wildcard match (e.g., as specified in url-pattern of a
+                     * jsp-property-group.
+                     * Force the context's welcome files, which are interpreted
+                     * as JSP files (since they match the url-pattern), to be
+                     * considered. See Bugzilla 27664.
+                     */ 
+                    mappingData.wrapper = null;
+                    checkJspWelcomeFiles = true;
+                } else {
+                    // See Bugzilla 27704
+                    mappingData.wrapperPath.setChars(buf, path.getStart(),
+                                                     path.getLength());
+                    mappingData.pathInfo.recycle();
+                }
+            }
+        }
+
+        if(mappingData.wrapper == null && noServletPath) {
+            // The path is empty, redirect to "/"
+            mappingData.redirectPath.setChars
+                (path.getBuffer(), pathOffset, pathEnd);
+            path.setEnd(pathEnd - 1);
+            //return;
+        }
+
+        // Rule 3 -- Extension Match
+        WrapperMapElement[] extensionWrappers = context.extensionWrappers;
+        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
+            internalMapExtensionWrapper(extensionWrappers, path, mappingData);
+        }
+
+        File file = null;
+        // Rule 4 -- Welcome resources processing for servlets
+        if (mappingData.wrapper == null) {
+            boolean checkWelcomeFiles = checkJspWelcomeFiles;
+            if (!checkWelcomeFiles) {
+                char[] buf = path.getBuffer();
+                checkWelcomeFiles = (buf[pathEnd - 1] == '/');
+            }
+            if (checkWelcomeFiles) {
+                for (int i = 0; (i < context.welcomeResources.length)
+                         && (mappingData.wrapper == null); i++) {
+                    path.setOffset(pathOffset);
+                    path.setEnd(pathEnd);
+                    path.append(context.welcomeResources[i], 0,
+                                context.welcomeResources[i].length());
+                    path.setOffset(servletPath);
+
+                    // Rule 4a -- Welcome resources processing for exact macth
+                    internalMapExactWrapper(exactWrappers, path, mappingData);
+
+                    // Rule 4b -- Welcome resources processing for prefix match
+                    if (mappingData.wrapper == null) {
+                        internalMapWildcardWrapper
+                            (wildcardWrappers, context.nesting, 
+                             path, mappingData);
+                    }
+
+                    // Rule 4c -- Welcome resources processing
+                    //            for physical folder
+                    if (mappingData.wrapper == null
+                        && context.resources != null) {
+                        // Default servlet: check if it's file or dir to apply
+                        // welcome files rules. 
+                        // TODO: Save the File in attributes, 
+                        // to avoid duplication in DefaultServlet.
+                        
+                        String pathStr = path.toString();
+                        file = new File(context.resources, pathStr);
+                        if (file.exists() && !(file.isDirectory()) ) {
+                            
+                            internalMapExtensionWrapper(extensionWrappers,
+                                                        path, mappingData);
+                            if (mappingData.wrapper == null
+                                && context.defaultWrapper != null) {
+                                mappingData.wrapper =
+                                    context.defaultWrapper.object;
+                                mappingData.requestPath.setChars
+                                    (path.getBuffer(), path.getStart(), 
+                                     path.getLength());
+                                mappingData.wrapperPath.setChars
+                                    (path.getBuffer(), path.getStart(), 
+                                     path.getLength());
+                                mappingData.requestPath.setString(pathStr);
+                                mappingData.wrapperPath.setString(pathStr);
+                            }
+                        }
+                    }
+                }
+
+                path.setOffset(servletPath);
+                path.setEnd(pathEnd);
+            }
+                                        
+        }
+
+
+        // Rule 7 -- Default servlet
+        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
+            if (context.defaultWrapper != null) {
+                mappingData.wrapper = context.defaultWrapper.object;
+                mappingData.requestPath.setChars
+                    (path.getBuffer(), path.getStart(), path.getLength());
+                mappingData.wrapperPath.setChars
+                    (path.getBuffer(), path.getStart(), path.getLength());
+            }
+            // Redirection to a folder
+            char[] buf = path.getBuffer();
+            if (context.resources != null && buf[pathEnd -1 ] != '/') {
+                String pathStr = path.toString();
+                file = new File( context.resources, pathStr);
+                if (file.exists() && file.isDirectory()) {
+                    // Note: this mutates the path: do not do any processing 
+                    // after this (since we set the redirectPath, there 
+                    // shouldn't be any)
+                    path.setOffset(pathOffset);
+                    path.append('/');
+                    mappingData.redirectPath.setChars
+                        (path.getBuffer(), path.getStart(), path.getLength());
+                } else {
+                    mappingData.requestPath.setString(pathStr);
+                    mappingData.wrapperPath.setString(pathStr);
+                }
+            }
+        }
+
+        path.setOffset(pathOffset);
+        path.setEnd(pathEnd);
+    }
+
+
+    /**
+     * Exact mapping.
+     */
+    private final void internalMapExactWrapper
+        (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) {
+        int pos = find(wrappers, path);
+        if ((pos != -1) && (path.equals(wrappers[pos].name))) {
+            mappingData.requestPath.setString(wrappers[pos].name);
+            mappingData.wrapperPath.setString(wrappers[pos].name);
+            mappingData.wrapper = wrappers[pos].object;
+        }
+    }
+
+
+    /**
+     * Wildcard mapping.
+     */
+    private final void internalMapWildcardWrapper
+        (WrapperMapElement[] wrappers, int nesting, CharChunk path, 
+         MappingData mappingData) {
+
+        int pathEnd = path.getEnd();
+        int pathOffset = path.getOffset();
+
+        int lastSlash = -1;
+        int length = -1;
+        int pos = find(wrappers, path);
+        if (pos != -1) {
+            boolean found = false;
+            while (pos >= 0) {
+                if (path.startsWith(wrappers[pos].name)) {
+                    length = wrappers[pos].name.length();
+                    if (path.getLength() == length) {
+                        found = true;
+                        break;
+                    } else if (path.startsWithIgnoreCase("/", length)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (lastSlash == -1) {
+                    lastSlash = nthSlash(path, nesting + 1);
+                } else {
+                    lastSlash = lastSlash(path);
+                }
+                path.setEnd(lastSlash);
+                pos = find(wrappers, path);
+            }
+            path.setEnd(pathEnd);
+            if (found) {
+                mappingData.wrapperPath.setString(wrappers[pos].name);
+                if (path.getLength() > length) {
+                    mappingData.pathInfo.setChars
+                        (path.getBuffer(),
+                         path.getOffset() + length,
+                         path.getLength() - length);
+                }
+                mappingData.requestPath.setChars
+                    (path.getBuffer(), path.getOffset(), path.getLength());
+                mappingData.wrapper = wrappers[pos].object;
+                mappingData.jspWildCard = wrappers[pos].jspWildCard;
+            }
+        }
+    }
+
+
+    /**
+     * Extension mappings.
+     */
+    private final void internalMapExtensionWrapper
+        (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) {
+        char[] buf = path.getBuffer();
+        int pathEnd = path.getEnd();
+        int servletPath = path.getOffset();
+        int slash = -1;
+        for (int i = pathEnd - 1; i >= servletPath; i--) {
+            if (buf[i] == '/') {
+                slash = i;
+                break;
+            }
+        }
+        if (slash == -1 ) slash = 0;
+        if (slash >= 0) {
+            int period = -1;
+            for (int i = pathEnd - 1; i > slash; i--) {
+                if (buf[i] == '.') {
+                    period = i;
+                    break;
+                }
+            }
+            if (period >= 0) {
+                path.setOffset(period + 1);
+                path.setEnd(pathEnd);
+                int pos = find(wrappers, path);
+                if ((pos != -1)
+                    && (path.equals(wrappers[pos].name))) {
+                    mappingData.wrapperPath.setChars
+                        (buf, servletPath, pathEnd - servletPath);
+                    mappingData.requestPath.setChars
+                        (buf, servletPath, pathEnd - servletPath);
+                    mappingData.wrapper = wrappers[pos].object;
+                }
+                path.setOffset(servletPath);
+                path.setEnd(pathEnd);
+            }
+        }
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    public static final int find(MapElement[] map, CharChunk name) {
+        return find(map, name, name.getStart(), name.getEnd());
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int find(MapElement[] map, CharChunk name,
+                                  int start, int end) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        
+        if (compare(name, start, end, map[0].name) < 0 ) {
+            return -1;
+        }         
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = compare(name, start, end, map[i].name);
+            if (result == 1) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = compare(name, start, end, map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int findIgnoreCase(MapElement[] map, CharChunk name) {
+        return findIgnoreCase(map, name, name.getStart(), name.getEnd());
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int findIgnoreCase(MapElement[] map, CharChunk name,
+                                  int start, int end) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) {
+            return -1;
+        }         
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = compareIgnoreCase(name, start, end, map[i].name);
+            if (result == 1) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = compareIgnoreCase(name, start, end, map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    public static final int find(MapElement[] map, String name) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        
+        if (name.compareTo(map[0].name) < 0) {
+            return -1;
+        } 
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = name.compareTo(map[i].name);
+            if (result > 0) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = name.compareTo(map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Compare given char chunk with String.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    private static final int compare(CharChunk name, int start, int end,
+                                     String compareTo) {
+        int result = 0;
+        char[] c = name.getBuffer();
+        int len = compareTo.length();
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (c[i + start] > compareTo.charAt(i)) {
+                result = 1;
+            } else if (c[i + start] < compareTo.charAt(i)) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length() > (end - start)) {
+                result = -1;
+            } else if (compareTo.length() < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Compare given char chunk with String ignoring case.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    private static final int compareIgnoreCase(CharChunk name, int start, int end,
+                                     String compareTo) {
+        int result = 0;
+        char[] c = name.getBuffer();
+        int len = compareTo.length();
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) {
+                result = 1;
+            } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length() > (end - start)) {
+                result = -1;
+            } else if (compareTo.length() < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Find the position of the last slash in the given char chunk.
+     */
+    public static final int lastSlash(CharChunk name) {
+
+        char[] c = name.getBuffer();
+        int end = name.getEnd();
+        int start = name.getStart();
+        int pos = end;
+
+        while (pos > start) {
+            if (c[--pos] == '/') {
+                break;
+            }
+        }
+
+        return (pos);
+
+    }
+
+
+    /**
+     * Find the position of the nth slash, in the given char chunk.
+     */
+    public static final int nthSlash(CharChunk name, int n) {
+
+        char[] c = name.getBuffer();
+        int end = name.getEnd();
+        int start = name.getStart();
+        int pos = start;
+        int count = 0;
+
+        while (pos < end) {
+            if ((c[pos++] == '/') && ((++count) == n)) {
+                pos--;
+                break;
+            }
+        }
+
+        return (pos);
+
+    }
+
+
+    /**
+     * Return the slash count in a given string.
+     */
+    public static final int slashCount(String name) {
+        int pos = -1;
+        int count = 0;
+        while ((pos = name.indexOf('/', pos + 1)) != -1) {
+            count++;
+        }
+        return count;
+    }
+
+
+    /**
+     * Insert into the right place in a sorted MapElement array, and prevent
+     * duplicates.
+     */
+    public static final boolean insertMap
+        (MapElement[] oldMap, MapElement[] newMap, MapElement newElement) {
+        int pos = find(oldMap, newElement.name);
+        if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
+            return false;
+        }
+        System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
+        newMap[pos + 1] = newElement;
+        System.arraycopy
+            (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
+        return true;
+    }
+
+
+    /**
+     * Insert into the right place in a sorted MapElement array.
+     */
+    public static final boolean removeMap
+        (MapElement[] oldMap, MapElement[] newMap, String name) {
+        int pos = find(oldMap, name);
+        if ((pos != -1) && (name.equals(oldMap[pos].name))) {
+            System.arraycopy(oldMap, 0, newMap, 0, pos);
+            System.arraycopy(oldMap, pos + 1, newMap, pos,
+                             oldMap.length - pos - 1);
+            return true;
+        }
+        return false;
+    }
+
+
+    // ------------------------------------------------- MapElement Inner Class
+
+
+    protected static abstract class MapElement {
+        /** hostname or path  
+         */
+        public String name = null;
+        public Object object = null;
+
+        public String toString() {
+            return "MapElement: \"" + name +"\"";
+        }
+    }
+
+
+    // ---------------------------------------------------- Context Inner Class
+
+
+    public static final class ContextMapElement
+        extends MapElement {
+
+        public String[] welcomeResources = new String[0];
+        public File resources = null;
+        public WrapperMapElement defaultWrapper = null;
+        public WrapperMapElement[] exactWrappers = new WrapperMapElement[0];
+        public WrapperMapElement[] wildcardWrappers = new WrapperMapElement[0];
+        public WrapperMapElement[] extensionWrappers = new WrapperMapElement[0];
+        public int nesting = 0;
+
+        public String toString() {
+            return "ContextMapElement {" +
+            "name: \"" + name +
+            "\"\nnesting: \"" + nesting +
+            "\"\n}";
+        }
+    }
+
+
+    // ---------------------------------------------------- Wrapper Inner Class
+
+
+    public static class WrapperMapElement
+        extends MapElement {
+        public boolean jspWildCard = false;
+    }
+   
+}

Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/http11/Http11Buffer.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/http11/Http11Buffer.java?view=auto&rev=534293
==============================================================================
--- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/http11/Http11Buffer.java (added)
+++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/http11/Http11Buffer.java Tue May  1 19:22:45 2007
@@ -0,0 +1,1384 @@
+/*
+ *  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.tomcat.lite.http11;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+
+import org.apache.tomcat.lite.util.MessageWriter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.UDecoder;
+import org.apache.tomcat.util.http.ContentType;
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.http.Parameters;
+
+/**
+ * Non-blocking parser for request line and headers. 
+ * 
+ * 
+ * Implementation of InputBuffer which provides HTTP request header parsing as
+ * well as transfer decoding.
+ * 
+ * Will also hold/cache headers. 
+ * 
+ * There is one Http11Buffer per connection. It doesn't hold a Request
+ * reference - Requests are per/thread and represent active operations.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author costin 
+ */
+public class Http11Buffer {
+
+    /**
+     * CRLF.
+     */
+    public static final String CRLF = "\r\n";
+
+    
+    /**
+     * Server string.
+     */
+    public static final byte[] SERVER_BYTES = 
+        "Server: Apache-Coyote/1.1\r\n".getBytes();
+
+    
+    /**
+     * CR.
+     */
+    public static final byte CR = (byte) '\r';
+
+
+    /**
+     * LF.
+     */
+    public static final byte LF = (byte) '\n';
+
+
+    /**
+     * SP.
+     */
+    public static final byte SP = (byte) ' ';
+
+
+    /**
+     * HT.
+     */
+    public static final byte HT = (byte) '\t';
+
+
+    /**
+     * COLON.
+     */
+    public static final byte COLON = (byte) ':';
+    
+    /**
+     * SEMI_COLON.
+     */
+    public static final byte SEMI_COLON = (byte) ';';
+
+    /**
+     * 'A'.
+     */
+    public static final byte A = (byte) 'A';
+
+
+    /**
+     * 'a'.
+     */
+    public static final byte a = (byte) 'a';
+
+
+    /**
+     * 'Z'.
+     */
+    public static final byte Z = (byte) 'Z';
+    
+
+    /**
+     * Lower case offset.
+     */
+    public static final byte LC_OFFSET = A - a;
+
+
+
+    /**
+     * '?'.
+     */
+    public static final byte QUESTION = (byte) '?';
+
+
+
+    /**
+     * HTTP/1.0.
+     */
+    public static final String HTTP_10 = "HTTP/1.0";
+
+    public static final byte[] _200_BYTES = {'2', '0', '0'};
+    
+    public static final byte[] _400_BYTES = {'4', '0' , '0'};
+
+    public static final byte[] _404_BYTES = { '4', '0', '4' }; 
+
+
+    /**
+     * HTTP/1.1.
+     */
+    public static final String HTTP_11 = "HTTP/1.1";
+    public static final byte[] HTTP_11_BYTES = HTTP_11.getBytes();
+
+    public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+
+    /**
+     * Default locale as mandated by the spec.
+     */
+    private static final Locale DEFAULT_LOCALE = Locale.getDefault();
+    
+    
+    /**
+     * Post data buffer.
+     */
+    public final static int CACHED_POST_LEN = 8192;
+    
+    // coyote uses 48K - not sure how arbitrary it is.
+    // should be <= output buffer size
+    private final static int HEAD_SIZE = 32 * 1026;
+    
+    // Fields.
+    
+    // Wrappers around the input buffer.
+    public MessageBytes methodMB = MessageBytes.newInstance();
+    public MessageBytes unparsedURIMB = MessageBytes.newInstance();
+    public MessageBytes uriMB = MessageBytes.newInstance();    
+    public MessageBytes decodedUriMB = MessageBytes.newInstance();
+    
+    public MessageBytes queryMB = MessageBytes.newInstance();
+    public MessageBytes protoMB = MessageBytes.newInstance();
+
+    public MimeHeaders headers = new MimeHeaders();
+
+    // Buffer 
+    
+    /** Last valid byte in the buf[]
+     */
+    public int lastValid;
+
+    /** Position in the buffer.
+     */
+    public int pos;
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    public byte[] buf;
+
+    public  byte[] postData = null;
+
+    // for output 
+    public MessageWriter messageWriter = new MessageWriter(8192);
+
+
+    // Options
+    /**
+     * Swallow input ? (in the case of an expectation)
+     */
+    protected boolean swallowInput;
+    /**
+     * Pointer to the US-ASCII header buffer.
+     */
+    protected char[] ascbuf;
+    // res
+    protected String contentType = null;
+    protected String contentLanguage = null;
+    protected String characterEncoding = DEFAULT_CHARACTER_ENCODING;
+
+    /**
+     * Has the charset been explicitly set.
+     */
+    protected boolean charsetSet = false;
+    // ---------------- Res
+    /**
+     * Holds request error exception.
+     */
+    protected Throwable errorException = null;
+    //Request req;
+    // Protocol components ( both req and response )
+    // TODO: move the relevant buffers here. Now using it as a struct{}
+    // status code
+    int status;
+
+    boolean commited;
+    String message;
+
+    
+    int resStatus = 200;
+
+    // restart from this position
+    int lastParsed = 0;
+
+    // 0: parsing request line
+    // 1: parsing headers
+    // 2: request done
+    int state = 0;
+    
+    static int STATE_REQUEST_LINE = 0;
+    static int STATE_HEADERS = 1;
+    static int STATE_BODY = 2;
+
+    // remote address/host
+    private int serverPort = -1;
+
+    private MessageBytes serverNameMB = MessageBytes.newInstance();
+
+
+    private int remotePort;
+    private int localPort;
+
+
+
+    private MessageBytes schemeMB = MessageBytes.newInstance();
+
+    private MessageBytes instanceId = MessageBytes.newInstance();
+
+    
+    /**
+     * URL decoder.
+     */
+    private UDecoder urlDecoder = new UDecoder();
+
+
+    /**
+     * HTTP specific fields. (remove them ?)
+     */
+    private long contentLength = -1;
+
+    private MessageBytes contentTypeMB = null;
+
+
+    private String charEncoding = null;
+    private Cookies cookies = new Cookies(headers);
+    private Parameters parameters = new Parameters();
+    private MessageBytes remoteUser=MessageBytes.newInstance();
+
+    private MessageBytes authType=MessageBytes.newInstance();
+    
+    private HashMap attributes=new HashMap();
+
+    
+    // --------------------------------------------------------- Public Methods
+
+    private int bytesRead=0;
+
+    // Time of the request - usefull to avoid repeated calls to System.currentTime
+    private long startTime = 0L;
+    
+    // General informations
+    private long bytesWritten=0;
+    
+
+    private Locale locale = DEFAULT_LOCALE;
+
+    public Http11Buffer() {
+        this(HEAD_SIZE);
+    }
+
+    public Http11Buffer(int headerBufferSize) {
+        //this.req = req;
+        buf = new byte[headerBufferSize];
+        // TODO: do we really need this ? How does it interact with MessageBytes ?
+        ascbuf = new char[headerBufferSize];
+
+        state = STATE_REQUEST_LINE;
+        swallowInput = true;
+    }
+
+
+    public void addHeader(String name, String value) {
+        char cc=name.charAt(0);
+        if( cc=='C' || cc=='c' ) {
+            if( checkSpecialHeader(name, value) )
+            return;
+        }
+        headers.addValue(name).setString( value );
+    }
+    
+    /**
+     * Warning: This method always returns <code>false<code> for Content-Type
+     * and Content-Length.
+     */
+    public boolean containsHeader(String name) {
+        return headers.getHeader(name) != null;
+    }
+
+    public MessageBytes decodedURI() {
+        return decodedUriMB;
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        buf[pos++] = CR;
+        buf[pos++] = LF;
+
+    }
+    
+    /**
+     * End request (consumes leftover bytes).
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+//        if (swallowInput && (lastActiveFilter != -1)) {
+//            int extraBytes = (int) activeFilters[lastActiveFilter].end();
+//            pos = pos - extraBytes;
+//        }
+
+    }
+
+    /**
+     * Get the character encoding used for this request.
+     */
+    public String getCharacterEncoding() {
+
+        if (charEncoding != null)
+            return charEncoding;
+
+        charEncoding = ContentType.getCharsetFromContentType(getContentType());
+        return charEncoding;
+
+    }
+
+    /**
+     * Return the content language.
+     */
+    public String getContentLanguage() {
+        return contentLanguage;
+    }
+
+
+
+
+
+    
+    public int getContentLength() {
+        long length = getContentLengthLong();
+        
+        if (length < Integer.MAX_VALUE) {
+            return (int) length;
+        }
+        return -1;
+    }
+    public long getContentLengthLong() {
+        return contentLength;
+    }
+    public String getContentType() {
+
+        String ret = contentType;
+
+        if (ret != null 
+            && characterEncoding != null
+            && charsetSet) {
+            ret = ret + ";charset=" + characterEncoding;
+        }
+
+        return ret;
+    }
+    public Cookies getCookies() {
+        return cookies;
+    }
+    
+    /** 
+     * Get the Exception that occurred during request
+     * processing.
+     */
+    public Throwable getErrorException() {
+        return errorException;
+    }
+
+    
+    // ---------------- Buffers 
+    public int getFreeSpace() {
+        return buf.length - lastValid;
+    }
+
+
+    public int getFreeSpace(int sz) {
+        int remain = getFreeSpace();
+        return (sz > remain) ? remain :sz;
+    }
+
+
+    public String getHeader(String name) {
+        return headers.getHeader(name);
+    }
+
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Get the status message.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    public MimeHeaders getMimeHeaders() {
+        return headers;
+    }
+    
+    public Parameters getParameters() {
+        return parameters;
+    }
+
+    public int getStatus() {
+        return resStatus;
+    }
+    
+    public UDecoder getURLDecoder() {
+        return urlDecoder;
+    }
+
+
+
+    public boolean isCommitted() {
+        return commited;
+    }
+    public boolean isExceptionPresent() {
+        return ( errorException != null );
+    }
+
+
+    public MessageBytes method() {
+        return methodMB;
+    }
+
+
+    
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest() {
+        swallowInput = true;
+        if (pos < lastValid) {
+            System.arraycopy(buf, pos, buf, 0, lastValid - pos);
+            lastValid -= pos;
+        } else {
+            lastValid = 0;
+        }
+        pos = 0;
+        state = STATE_REQUEST_LINE;
+        swallowInput = true;
+    }
+
+
+    /**
+     * Parse HTTP headers.
+     * 
+     * @return false when not done, true when all headers have been parsed.
+     */
+    public boolean parseHeaders()
+            throws IOException {
+        
+        // lastParsed is the end of the previous line ( request line or header )
+        pos = lastParsed;
+        
+        while( state == STATE_HEADERS) {
+            // Check for blank line
+            byte chr = 0;
+
+            // End of headers or ? 
+            while (true) {
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    return false;
+                }
+                chr = buf[pos];
+                if ((chr == CR) || (chr == LF)) {
+                    if (chr == LF) {
+                        pos++;
+                        // Done with the headers
+                        state = STATE_BODY;
+                        lastParsed = pos;
+                        return true;
+                    } // else: CR - keep going
+                } else {
+                    break; // real char
+                }
+                pos++;
+            }
+    
+            // Mark the current buffer position
+            int start = pos;
+            int headerStart = pos;
+            // Reading the header name. Header name is always US-ASCII
+            boolean colon = false;
+            int colonPos = pos;
+    
+            while (!colon) {
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    return false;
+                }
+                if (buf[pos] == COLON) {
+                    colon = true;
+                    colonPos = pos;
+                }
+                chr = buf[pos];
+                // Convert header to-lower case ??? Modify the buffer ?
+                // TODO: FIX THIS
+                if ((chr >= A) && (chr <= Z)) {
+                    buf[pos] = (byte) (chr - LC_OFFSET);
+                }
+                ascbuf[pos] = (char) buf[pos];
+                pos++;
+            }
+    
+            // Mark the current buffer position
+            start = pos;
+            int realPos = pos;
+    
+            // Reading the header value (which can be spanned on multiple lines)
+            boolean eol = false;
+            boolean validLine = true;
+
+            while (validLine) {
+                boolean space = true;
+                // Skipping spaces
+                while (space) {
+                    if ((buf[pos] == SP) || 
+                            (buf[pos] == HT)) {
+                        if (pos >= lastValid) return false;
+                        pos++;
+                    } else {
+                        space = false;
+                    }
+                }
+    
+                // Reading bytes until the end of the line. Also move the 
+                // bytes to normalize the value
+                int lastSignificantChar = realPos;
+                // TODO: remove normalization, do it at the end or never
+                while (!eol) {
+                    if (buf[pos] == CR) {
+                    } else if (buf[pos] == LF) {
+                        eol = true;
+                    } else if (buf[pos] == SP) {
+                        buf[realPos] = buf[pos];
+                        realPos++;
+                    } else {
+                        buf[realPos] = buf[pos];
+                        buf[pos] = SP; // so next time we skip it.
+                        realPos++;
+                        lastSignificantChar = realPos;
+                    }
+    
+                    if (pos >= lastValid) return false;
+                    pos++;
+                }
+    
+                realPos = lastSignificantChar;
+    
+                // Checking the first character of the new line. If the character
+                // is a LWS, then it's a multiline header
+                // Read new bytes if needed
+                if (pos >= lastValid) return false;
+    
+                chr = buf[pos];
+                if ((chr != SP) && (chr != HT)) {
+                    validLine = false;
+                } else {
+                    eol = false;
+                    // Copying one extra space in the buffer (since there must
+                    // be at least one space inserted between the lines)
+                    buf[realPos] = chr;
+                    realPos++;
+                }
+            }
+    
+            // Set the header value
+            MessageBytes headerValue = 
+                headers.addValue(ascbuf, headerStart, 
+                        colonPos - headerStart);
+            headerValue.setBytes(buf, start, realPos - start);
+            
+            lastParsed = pos;
+        }
+        
+        state = STATE_BODY;
+        lastParsed = pos;
+        return true;
+    }
+
+    
+    /**
+     * Read the request line. This function is meant to be used during the 
+     * HTTP request header parsing. Do NOT attempt to read the request body 
+     * using it.
+     *
+     * @throws IOException If an exception occurs during the underlying socket
+     * read operations, or if the given buffer is not big enough to accomodate
+     * the whole line.
+     */
+    public boolean parseRequestLine()
+            throws IOException {
+
+        int start = 0;
+        
+        lastParsed = pos;
+        state = STATE_REQUEST_LINE;
+        
+        // Skipping blank lines
+        byte chr = 0;
+        do {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                return false;
+            }
+
+            chr = buf[pos++];
+
+        } while ((chr == CR) || (chr == LF));
+
+        pos--;
+
+        // Mark the current buffer position
+        start = pos;
+
+        //
+        // Reading the method name
+        // Method name is always US-ASCII
+        //
+        boolean space = false;
+
+        while (!space) {
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                return false;
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == SP) {
+                space = true;
+                methodMB.setChars(ascbuf, start, pos - start);
+            }
+            pos++;
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int end = 0;
+        int questionPos = -1;
+
+        // Reading the URI
+        space = false;
+        boolean eol = false;
+        while (!space) {
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                return false;
+            }
+            if (buf[pos] == SP) {
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == CR) 
+                       || (buf[pos] == LF)) {
+                // HTTP/0.9 style request
+                eol = true;
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == QUESTION) 
+                       && (questionPos == -1)) {
+                questionPos = pos;
+            }
+            pos++;
+        }
+
+        unparsedURIMB.setBytes(buf, start, end - start);
+        if (questionPos >= 0) {
+            queryMB.setBytes(buf, questionPos + 1, 
+                                           end - questionPos - 1);
+            uriMB.setBytes(buf, start, questionPos - start);
+        } else {
+            uriMB.setBytes(buf, start, end - start);
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        end = 0;
+
+        // Reading the protocol. Protocol is always US-ASCII
+        while (!eol) {
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                return false;
+            }
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == CR) {
+                end = pos;
+            } else if (buf[pos] == LF) {
+                if (end == 0)
+                    end = pos;
+                eol = true;
+            }
+            pos++;
+        }
+
+        if ((end - start) > 0) {
+            protoMB.setChars(ascbuf, start, end - start);
+        } else {
+            protoMB.setString("");
+        }
+        
+        state = STATE_HEADERS;
+        lastParsed = pos;
+        return true;
+    }
+
+    public boolean parseResponseLine()
+            throws IOException {
+        int start = 0;
+        lastParsed = pos;
+        state = STATE_REQUEST_LINE;
+
+        //      Mark the current buffer position
+        start = pos;
+
+
+        //  Protocol is always US-ASCII
+        boolean space = false;
+
+        while (!space) {
+            ascbuf[pos] = (char) buf[pos];
+            if (buf[pos] == SP) {
+                space = true;
+                methodMB.setChars(ascbuf, start, pos - start);
+            }
+            if (pos >= lastValid) return false;
+            pos++;
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int end = 0;
+        
+        // Read status code
+        space = false;
+        boolean eol = false;
+        while (!space) {
+            if (pos >= lastValid) return false;
+            
+            if (buf[pos] == SP) {
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == CR) 
+                    || (buf[pos] == LF)) {
+                // HTTP/0.9 style request
+                eol = true;
+                space = true;
+                end = pos;
+            }
+            pos++;
+        }
+
+        //      Mark the current buffer position
+        start = pos;
+        end = 0;
+
+        // Status message
+        while (!eol) {
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                return false;
+            }
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == CR) {
+                end = pos;
+            } else if (buf[pos] == LF) {
+                if (end == 0)
+                    end = pos;
+                eol = true;
+            }
+            pos++;
+        }
+
+        if ((end - start) > 0) {
+            protoMB.setChars(ascbuf, start, end - start);
+        } else {
+            protoMB.setString("");
+        }
+
+        state = STATE_HEADERS;
+        lastParsed = pos;
+        return true;
+    }
+
+    public MessageBytes protocol() {
+        return protoMB;
+    }
+    
+    public MessageBytes queryString() {
+        return queryMB;
+    }
+
+    /**
+     * Recycle the input buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+        lastValid = 0;
+        pos = 0;
+        swallowInput = true;
+        
+        messageWriter.recycle();
+        
+        resStatus = 200;
+        contentType = null;
+        contentLanguage = null;
+        locale = DEFAULT_LOCALE;
+        characterEncoding = DEFAULT_CHARACTER_ENCODING;
+        charsetSet = false;
+        contentLength = -1;
+        message = null;
+        commited = false;
+        errorException = null;
+        headers.clear();
+
+        
+        // update counters
+        bytesWritten=0;
+        
+        bytesRead=0;
+
+        contentLength = -1;
+        contentTypeMB = null;
+        charEncoding = null;
+        headers.recycle();
+        serverNameMB.recycle();
+        serverPort=-1;
+        localPort = -1;
+        remotePort = -1;
+
+        cookies.recycle();
+        parameters.recycle();
+
+        unparsedURIMB.recycle();
+        uriMB.recycle(); 
+        decodedUriMB.recycle();
+        queryMB.recycle();
+        methodMB.recycle();
+        protoMB.recycle();
+
+        schemeMB.recycle();
+
+        instanceId.recycle();
+        remoteUser.recycle();
+        authType.recycle();
+        attributes.clear();
+
+
+    }
+    public MessageBytes requestURI() {
+        return uriMB;
+    }
+
+    
+    public void reset() 
+        throws IllegalStateException {
+
+        // Reset the headers only if this is the main request,
+        // not for included
+        contentType = null;
+        locale = DEFAULT_LOCALE;
+        contentLanguage = null;
+        characterEncoding = DEFAULT_CHARACTER_ENCODING;
+        contentLength = -1;
+        charsetSet = false;
+
+        status = 200;
+        message = null;
+        headers.clear();
+
+        // Force the PrintWriter to flush its data to the output
+        // stream before resetting the output stream
+        //
+        // Reset the stream
+        if (commited) {
+            //String msg = sm.getString("servletOutputStreamImpl.reset.ise"); 
+            throw new IllegalStateException();
+        }
+    }
+    public MessageBytes scheme() {
+        return schemeMB;
+    }
+    
+    
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        buf[pos++] = COLON;
+        buf[pos++] = SP;
+        write(value);
+        buf[pos++] = CR;
+        buf[pos++] = LF;
+
+    }
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(MessageBytes name, MessageBytes value) {
+
+        write(name);
+        buf[pos++] = COLON;
+        buf[pos++] = SP;
+        write(value);
+        buf[pos++] = CR;
+        buf[pos++] = LF;
+
+    }
+    
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        buf[pos++] = COLON;
+        buf[pos++] = SP;
+        write(value);
+        buf[pos++] = CR;
+        buf[pos++] = LF;
+
+    }
+
+    /**
+     * Send the response status line.
+     */
+    public void sendStatus(int status, String message) {
+
+        // Write protocol name
+        write(HTTP_11_BYTES);
+        buf[pos++] = SP;
+
+        // Write status code
+        switch (status) {
+        case 200:
+            write(_200_BYTES);
+            break;
+        case 400:
+            write(_400_BYTES);
+            break;
+        case 404:
+            write(_404_BYTES);
+            break;
+        default:
+            write(status);
+        }
+
+        buf[pos++] = SP;
+
+        // Write message
+        if (message == null) {
+            write(getMessage(status));
+        } else {
+            write(message);
+        }
+
+        buf[pos++] = CR;
+        buf[pos++] = LF;
+    }
+
+    public void setCharacterEncoding(String enc) {
+        this.charEncoding = enc;
+    }
+
+    public void setCommitted(boolean v) {
+        this.commited = v;
+    }
+
+    public void setContentLength(int contentLength) {
+        this.contentLength = contentLength;
+    }
+    public void setContentLength(long contentLength) {
+        this.contentLength = contentLength;
+    } 
+    
+    /**
+     * Sets the content type.
+     *
+     * This method must preserve any response charset that may already have 
+     * been set via a call to response.setContentType(), response.setLocale(),
+     * or response.setCharacterEncoding().
+     *
+     * @param type the content type
+     */
+    public void setContentType(String type) {
+
+        int semicolonIndex = -1;
+
+        if (type == null) {
+            this.contentType = null;
+            return;
+        }
+
+        /*
+         * Remove the charset param (if any) from the Content-Type, and use it
+         * to set the response encoding.
+         * The most recent response encoding setting will be appended to the
+         * response's Content-Type (as its charset param) by getContentType();
+         */
+        boolean hasCharset = false;
+        int len = type.length();
+        int index = type.indexOf(';');
+        while (index != -1) {
+            semicolonIndex = index;
+            index++;
+            while (index < len && Character.isSpace(type.charAt(index))) {
+                index++;
+            }
+            if (index+8 < len
+                    && type.charAt(index) == 'c'
+                    && type.charAt(index+1) == 'h'
+                    && type.charAt(index+2) == 'a'
+                    && type.charAt(index+3) == 'r'
+                    && type.charAt(index+4) == 's'
+                    && type.charAt(index+5) == 'e'
+                    && type.charAt(index+6) == 't'
+                    && type.charAt(index+7) == '=') {
+                hasCharset = true;
+                break;
+            }
+            index = type.indexOf(';', index);
+        }
+
+        if (!hasCharset) {
+            this.contentType = type;
+            return;
+        }
+
+        this.contentType = type.substring(0, semicolonIndex);
+        String tail = type.substring(index+8);
+        int nextParam = tail.indexOf(';');
+        String charsetValue = null;
+        if (nextParam != -1) {
+            this.contentType += tail.substring(nextParam);
+            charsetValue = tail.substring(0, nextParam);
+        } else {
+            charsetValue = tail;
+        }
+
+        // The charset value may be quoted, but must not contain any quotes.
+        if (charsetValue != null && charsetValue.length() > 0) {
+            charsetSet=true;
+            charsetValue = charsetValue.replace('"', ' ');
+            this.characterEncoding = charsetValue.trim();
+        }
+    }
+
+    /** 
+     * Set the error Exception that occurred during
+     * request processing.
+     */
+    public void setErrorException(Exception ex) {
+        errorException = ex;
+    }
+
+
+    
+    public void setHeader(String name, String value) {
+        char cc=name.charAt(0);
+        if( cc=='C' || cc=='c' ) {
+            if( checkSpecialHeader(name, value) )
+            return;
+        }
+        headers.setValue(name).setString( value);
+    }
+
+
+    // -------------------------- Output methods ----------------------
+    // Used for response and new request generation
+    
+    /**
+     * Called explicitely by user to set the Content-Language and
+     * the default encoding
+     */
+    public void setLocale(Locale locale) {
+
+        if (locale == null) {
+            return;  // throw an exception?
+        }
+
+        // Save the locale for use by getLocale()
+        this.locale = locale;
+
+        // Set the contentLanguage for header output
+        contentLanguage = locale.getLanguage();
+        if ((contentLanguage != null) && (contentLanguage.length() > 0)) {
+            String country = locale.getCountry();
+            StringBuffer value = new StringBuffer(contentLanguage);
+            if ((country != null) && (country.length() > 0)) {
+                value.append('-');
+                value.append(country);
+            }
+            contentLanguage = value.toString();
+        }
+
+    }
+
+
+    /**
+     * Set the status message.
+     */
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+
+    /** 
+     * Set the response status 
+     */ 
+    public void setStatus( int status ) {
+        this.resStatus = status;
+    }
+
+
+    /**
+     * Set the swallow input flag.
+     */
+    public void setSwallowInput(boolean swallowInput) {
+        this.swallowInput = swallowInput;
+    }
+
+
+    public String toString() {
+        return new String(buf, 0, lastValid);
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied byte 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param b data to be written
+     */
+    public void write(byte[] b) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(b, 0, buf, pos, b.length);
+        pos = pos + b.length;
+
+    }
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param bc data to be written
+     */
+    protected void write(ByteChunk bc) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos,
+                         bc.getLength());
+        pos = pos + bc.getLength();
+
+    }
+
+    protected void write(CharSequence cc) {
+      for (int i = 0; i < cc.length(); i++) {
+          char c = cc.charAt(i);
+          // Note:  This is clearly incorrect for many strings,
+          // but is the only consistent approach within the current
+          // servlet framework.  It must suffice until servlet output
+          // streams properly encode their output.
+          if ((c <= 31) && (c != 9)) {
+              c = ' ';
+          } else if (c == 127) {
+              c = ' ';
+          }
+          buf[pos++] = (byte) c;
+      }
+    }
+    
+    /**
+     * This method will print the specified integer to the output stream, 
+     * without filtering. This method is meant to be used to write the 
+     * response header.
+     * 
+     * @param i data to be written
+     */
+    protected void write(int i) {
+
+        write(String.valueOf(i));
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param mb data to be written
+     */
+    protected void write(MessageBytes mb) {
+
+        if (mb.isBytes()) {
+            ByteChunk bc = mb.getByteChunk();
+            write(bc);
+        } else {
+            write(mb.getCharSequence());
+        }
+
+    }
+
+    /** 
+     * Set internal fields for special header names. 
+     * Called from set/addHeader.
+     * Return true if the header is special, no need to set the header.
+     */
+    private boolean checkSpecialHeader( String name, String value) {
+        // XXX Eliminate redundant fields !!!
+        // ( both header and in special fields )
+        if( name.equalsIgnoreCase( "Content-Type" ) ) {
+            setContentType( value );
+            return true;
+        }
+        if( name.equalsIgnoreCase( "Content-Length" ) ) {
+            try {
+                long cL=Long.parseLong( value );
+                setContentLength( cL );
+                return true;
+            } catch( NumberFormatException ex ) {
+                // Do nothing - the spec doesn't have any "throws" 
+                // and the user might know what he's doing
+                return false;
+            }
+        }
+        if( name.equalsIgnoreCase( "Content-Language" ) ) {
+            // XXX XXX Need to construct Locale or something else
+        }
+        return false;
+    }
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+//    /**
+//     * Read some bytes.
+//     */
+//    public int doRead(ByteChunk chunk, Request req) 
+//        throws IOException {
+//
+//        if (lastActiveFilter == -1)
+//            return inputStreamInputBuffer.doRead(chunk, req);
+//        else
+//            return activeFilters[lastActiveFilter].doRead(chunk,req);
+//
+//    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+
+//    /**
+//     * Fill the internal buffer using data from the undelying input stream.
+//     * 
+//     * @return false if at end of stream
+//     */
+//    protected boolean fill()
+//        throws IOException {
+//
+//        int nRead = 0;
+//
+//        if (parsingHeader) {
+//
+//            if (lastValid == buf.length) {
+//                throw new IOException
+//                    ();
+//            }
+//
+//            nRead = inputStream.read(buf, pos, buf.length - lastValid);
+//            if (nRead > 0) {
+//                lastValid = pos + nRead;
+//            }
+//
+//        } else {
+//
+//            buf = bodyBuffer;
+//            pos = 0;
+//            lastValid = 0;
+//            nRead = inputStream.read(buf, 0, buf.length);
+//            if (nRead > 0) {
+//                lastValid = nRead;
+//            }
+//
+//        }
+//
+//        return (nRead > 0);
+//
+//    }
+
+//        public int doRead(ByteChunk chunk, Request req ) 
+//            throws IOException {
+//
+//            if (pos >= lastValid) {
+//                if (!fill())
+//                    return -1;
+//            }
+//
+//            int length = lastValid - pos;
+//            chunk.setBytes(buf, pos, length);
+//            pos = lastValid;
+
+    private String getMessage(final int message){
+        return HttpMessages.getMessage(message);
+    }
+}



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


Mime
View raw message