tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cos...@apache.org
Subject svn commit: r761964 [10/10] - in /tomcat/trunk/modules/tomcat-lite/java: ./ org/ org/apache/ org/apache/tomcat/ org/apache/tomcat/addons/ org/apache/tomcat/integration/ org/apache/tomcat/integration/jmx/ org/apache/tomcat/integration/simple/ org/apache...
Date Sat, 04 Apr 2009 16:24:36 GMT
Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/SimpleSessionManager.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/SimpleSessionManager.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/SimpleSessionManager.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/SimpleSessionManager.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,545 @@
+/*
+ * 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.servlets.session;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.addons.UserSessionManager;
+
+// TODO: move 'expiring objects' to a separate utility class
+// TODO: hook the background thread
+
+/**
+ * Minimal implementation of the <b>Manager</b> interface that supports
+ * no session persistence or distributable capabilities.  This class may
+ * be subclassed to create more sophisticated Manager implementations.
+ * 
+ * @author Costin Manolache
+ * @author Craig R. McClanahan
+ */
+public class SimpleSessionManager implements UserSessionManager {
+    protected static Log log = LogFactory.getLog(SimpleSessionManager.class);
+
+    protected RandomGenerator randomG = new RandomGenerator();
+    
+    protected ServletContext context;
+
+
+    /**
+     * The distributable flag for Sessions created by this Manager.  If this
+     * flag is set to <code>true</code>, any user attributes added to a
+     * session controlled by this Manager must be Serializable. 
+     * 
+     * This is for compliance with the spec - tomcat-lite is not intended for
+     * session replication ( use a full version for that )
+     */
+    protected boolean distributable;
+
+    /**
+     * The default maximum inactive interval for Sessions created by
+     * this Manager.
+     */
+    protected int maxInactiveInterval = 60;
+
+    /**
+     * The longest time (in seconds) that an expired session had been alive.
+     */
+    protected int sessionMaxAliveTime;
+
+
+    /**
+     * Average time (in seconds) that expired sessions had been alive.
+     */
+    protected int sessionAverageAliveTime;
+
+
+    /**
+     * Number of sessions that have expired.
+     */
+    protected int expiredSessions = 0;
+
+    static class SessionLRU extends LinkedHashMap {
+        protected boolean removeEldestEntry(Map.Entry eldest) {
+            HttpSessionImpl s = (HttpSessionImpl)eldest.getValue();
+            int size = this.size();
+            
+            // TODO: check if eldest is expired or if we're above the limit.
+            // if eldest is expired, turn a flag to check for more.
+            
+            // Note: this doesn't work well for sessions that set shorter
+            // expiry time, or longer expiry times. 
+            return false;
+        }
+
+    }
+    
+    /**
+     * The set of currently active Sessions for this Manager, keyed by
+     * session identifier.
+     */
+    protected LinkedHashMap sessions = new SessionLRU();
+        
+    // Number of sessions created by this manager
+    protected int sessionCounter=0;
+
+    protected int maxActive=0;
+
+    // number of duplicated session ids - anything >0 means we have problems
+    protected int duplicates=0;
+
+    protected boolean initialized=false;
+    
+    /**
+     * Processing time during session expiration.
+     */
+    protected long processingTime = 0;
+    
+    static List EMPTY_LIST = new ArrayList();
+
+    // One per machine - it has an internal pool, can schedule 
+    // tasks for multiple webapps.
+    static Timer timer = new Timer();
+    boolean active = false;
+    
+    TimerTask task = new TimerTask() {
+        public void run() {
+            processExpires();
+            synchronized (sessions) {
+                // We don't want a timer thread running around if 
+                // there is no activity
+                if (sessions.size() == 0) {
+                    active = false;
+                    this.cancel();
+                }
+            }
+        }
+    };
+
+    public List getEventListeners() {
+        List l = 
+            (List) context.getAttribute("context-listeners");
+        if (l == null) return EMPTY_LIST;
+        return l;
+    }
+    
+    /** 
+     * Total sessions created by this manager.
+     */
+    public int getSessionCounter() {
+        return sessionCounter;
+    }
+
+
+    /** 
+     * Number of duplicated session IDs generated by the random source.
+     * Anything bigger than 0 means problems.
+     *
+     * @return The count of duplicates
+     */
+    public int getDuplicates() {
+        return duplicates;
+    }
+
+
+    /** 
+     * Returns the number of active sessions
+     *
+     * @return number of sessions active
+     */
+    public int getActiveSessions() {
+        return sessions.size();
+    }
+
+
+    /**
+     * Max number of concurrent active sessions
+     *
+     * @return The highest number of concurrent active sessions
+     */
+    public int getMaxActive() {
+        return maxActive;
+    }
+
+
+    public void setMaxActive(int maxActive) {
+        this.maxActive = maxActive;
+    }
+
+
+    /**
+     * Gets the longest time (in seconds) that an expired session had been
+     * alive.
+     *
+     * @return Longest time (in seconds) that an expired session had been
+     * alive.
+     */
+    public int getSessionMaxAliveTime() {
+        return sessionMaxAliveTime;
+    }
+
+    /**
+     * Gets the average time (in seconds) that expired sessions had been
+     * alive.
+     *
+     * @return Average time (in seconds) that expired sessions had been
+     * alive.
+     */
+    public int getSessionAverageAliveTime() {
+        return sessionAverageAliveTime;
+    }
+
+    /**
+     * Return the Container with which this Manager is associated.
+     */
+    public ServletContext getContext() {
+        return (this.context);
+    }
+
+
+    /**
+     * Set the Container with which this Manager is associated.
+     *
+     * @param container The newly associated Container
+     */
+    public void setContext(ServletContext container) {
+        this.context = container;
+    }
+
+    /**
+     * Return the distributable flag for the sessions supported by
+     * this Manager.
+     */
+    public boolean getDistributable() {
+        return (this.distributable);
+    }
+
+    /**
+     * Set the distributable flag for the sessions supported by this
+     * Manager.  If this flag is set, all user data objects added to
+     * sessions associated with this manager must implement Serializable.
+     *
+     * @param distributable The new distributable flag
+     */
+    public void setDistributable(boolean distributable) {
+        this.distributable = distributable;
+    }
+
+    /**
+     * Return the default maximum inactive interval (in seconds)
+     * for Sessions created by this Manager.
+     */
+    public int getSessionTimeout() {
+        return (this.maxInactiveInterval);
+    }
+
+    public void setSessionTimeout(int stout) {
+        maxInactiveInterval = stout;
+    }
+
+    /**
+     * Gets the number of sessions that have expired.
+     *
+     * @return Number of sessions that have expired
+     */
+    public int getExpiredSessions() {
+        return expiredSessions;
+    }
+
+    /**
+     * Called when a session is expired, add the time to statistics
+     */
+    public void addExpiredSession(int timeAlive) {
+        synchronized (this) {
+            this.expiredSessions++; // should be atomic
+            // not sure it's the best solution
+            sessionAverageAliveTime = 
+                ((sessionAverageAliveTime * (expiredSessions-1)) + 
+                        timeAlive)/expiredSessions;
+            if (timeAlive > sessionMaxAliveTime) {
+                sessionMaxAliveTime = timeAlive;
+            }
+        }
+    }
+
+    public long getProcessingTime() {
+        return processingTime;
+    }
+
+    private void enableTimer() {
+        if (active) {
+            return;
+        }
+        active = true;
+        timer.scheduleAtFixedRate(task, getSessionTimeout(), getSessionTimeout());
+    }
+
+    
+    
+    /**
+     * Invalidate all sessions that have expired.
+     */
+    public void processExpires() {
+
+        long timeNow = System.currentTimeMillis();
+        HttpSessionImpl sessions[] = findSessions();
+        int expireHere = 0 ;
+        
+        if(log.isDebugEnabled())
+            log.debug("Start expire sessions "  + " at " + timeNow + " sessioncount " + sessions.length);
+        
+        for (int i = 0; i < sessions.length; i++) {
+            if (!sessions[i].isValid()) {
+                expiredSessions++;
+                expireHere++;
+            }
+        }
+        
+        long timeEnd = System.currentTimeMillis();
+        if(log.isDebugEnabled())
+             log.debug("End expire sessions " + " processingTime " + 
+                       (timeEnd - timeNow) + " expired sessions: " + expireHere);
+        
+        processingTime += ( timeEnd - timeNow );
+
+    }
+
+    public SimpleSessionManager() {
+        randomG.init();        
+    }
+    
+    public void destroy() {
+        initialized=false;
+    }
+
+    /**
+     * Add this Session to the set of active Sessions for this Manager.
+     *
+     * @param session Session to be added
+     */
+    public void add(HttpSessionImpl session) {
+        synchronized (sessions) {
+            sessions.put(session.getId(), session);
+            if( sessions.size() > maxActive ) {
+                maxActive=sessions.size();
+            }
+            
+            // Make sure the timer is set.
+            enableTimer();
+        }
+    }
+
+    
+    /**
+     * Construct and return a new session object, based on the default
+     * settings specified by this Manager's properties.  The session
+     * id specified will be used as the session id.  
+     * If a new session cannot be created for any reason, return 
+     * <code>null</code>.
+     * 
+     * @param sessionId The session id which should be used to create the
+     *  new session; if <code>null</code>, a new session id will be
+     *  generated
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     */
+    public HttpSessionImpl createSession(String sessionId) {
+        
+        // Recycle or create a Session instance
+        HttpSessionImpl session = createEmptySession();
+
+        // Initialize the properties of the new session and return it
+        session.setNew(true);
+        session.setValid(true);
+        session.setCreationTime(System.currentTimeMillis());
+        session.setMaxInactiveInterval(this.maxInactiveInterval);
+        if (sessionId == null ) {
+            while (sessionId == null) {
+                sessionId = randomG.generateSessionId();
+                if (sessions.get(sessionId) != null) {
+                    duplicates++;
+                    sessionId = null;    
+                }
+            }
+        }
+/*        } else {
+        // FIXME: Code to be used in case route replacement is needed
+            String jvmRoute = randomG.jvmRoute;
+            if (jvmRoute != null) {
+                String requestJvmRoute = null;
+                int index = sessionId.indexOf(".");
+                if (index > 0) {
+                    requestJvmRoute = sessionId
+                            .substring(index + 1, sessionId.length());
+                }
+                if (requestJvmRoute != null && !requestJvmRoute.equals(jvmRoute)) {
+                    sessionId = sessionId.substring(0, index) + "." + jvmRoute;
+                }
+            }
+*/
+        session.setId(sessionId);
+        sessionCounter++;
+        return (session);
+    }
+    
+    
+    /**
+     * Get a session from the recycled ones or create a new empty one.
+     * The PersistentManager manager does not need to create session data
+     * because it reads it from the Store.
+     */
+    public HttpSessionImpl createEmptySession() {
+        return new HttpSessionImpl(this);
+    }
+
+
+    /**
+     * Return the active Session, associated with this Manager, with the
+     * specified session id (if any); otherwise return <code>null</code>.
+     *
+     * @param id The session id for the session to be returned
+     *
+     * @exception IllegalStateException if a new session cannot be
+     *  instantiated for any reason
+     * @exception IOException if an input/output error occurs while
+     *  processing this request
+     */
+    public HttpSessionImpl findSession(String id) throws IOException {
+
+        if (id == null)
+            return (null);
+        synchronized (sessions) {
+            HttpSessionImpl session = (HttpSessionImpl) sessions.get(id);
+            return (session);
+        }
+
+    }
+
+
+    /**
+     * Return the set of active Sessions associated with this Manager.
+     * If this Manager has no active Sessions, a zero-length array is returned.
+     */
+    public HttpSessionImpl[] findSessions() {
+
+        HttpSessionImpl results[] = null;
+        synchronized (sessions) {
+            results = new HttpSessionImpl[sessions.size()];
+            results = (HttpSessionImpl[]) sessions.values().toArray(results);
+        }
+        return (results);
+
+    }
+
+
+    /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session Session to be removed
+     */
+    public void remove(HttpSessionImpl session) {
+
+        synchronized (sessions) {
+            sessions.remove(session.getId());
+        }
+
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+    /** JMX and debugging
+     */
+    public void expireSession( String sessionId ) {
+        HttpSessionImpl s=(HttpSessionImpl)sessions.get(sessionId);
+        if( s==null ) {
+            return;
+        }
+        s.expire();
+    }
+
+
+    /** JMX method or debugging
+     */
+    public String getLastAccessedTime( String sessionId ) {
+        HttpSessionImpl s=(HttpSessionImpl)sessions.get(sessionId);
+        if( s==null ) {
+            return "";
+        }
+        return new Date(s.getLastAccessedTime()).toString();
+    }
+
+    ThreadLocal httpSession;
+    
+    public String getSessionCookieName() {
+        return "JSESSIONID";
+    }
+    
+    
+    /** Parse the cookies. Since multiple session cookies could be set ( 
+     * different paths for example ), we need to lookup and find a valid one
+     * for our context. 
+     * 
+     * If none is found - the last (bad) session id is returned.
+     * 
+     * As side effect, an attribute is set on the req with the session ( we 
+     * already looked it up while searching ).
+     */
+    public HttpSession getRequestedSessionId(HttpServletRequest req) {
+        Cookie[] cookies = req.getCookies();
+        String cn = getSessionCookieName();
+        for (int i=0; i<cookies.length; i++) {
+            if (cn.equals(cookies[i].getName())) {
+                String id = cookies[i].getValue();
+                // TODO: mark session from cookie, check validity
+            }
+        }
+        return null;
+    }
+    
+    public String getSessionIdFromUrl(HttpServletRequest req) {
+        
+        return null;
+    }
+    
+    
+    public boolean isValid(HttpSession session) {
+      return ((HttpSessionImpl) session).isValid();
+    }
+
+    public void endAccess(HttpSession session) {
+      ((HttpSessionImpl) session).endAccess();
+    }
+
+    public void access(HttpSession session) {
+      ((HttpSessionImpl) session).access();
+    }
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/session/SimpleSessionManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.servlets.util;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+
+/**
+ * Adapter class that wraps an <code>Enumeration</code> around a Java2
+ * collection classes object <code>Iterator</code> so that existing APIs
+ * returning Enumerations can easily run on top of the new collections.
+ * Constructors are provided to easliy create such wrappers.
+ *
+ * @author Craig R. McClanahan
+ */
+public final class Enumerator implements Enumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     */
+    public Enumerator(Collection collection) {
+
+        this(collection.iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Collection.
+     *
+     * @param collection Collection whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Collection collection, boolean clone) {
+
+        this(collection.iterator(), clone);
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     */
+    public Enumerator(Iterator iterator) {
+
+        super();
+        this.iterator = iterator;
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values returned by the
+     * specified Iterator.
+     *
+     * @param iterator Iterator to be wrapped
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Iterator iterator, boolean clone) {
+
+        super();
+        if (!clone) {
+            this.iterator = iterator;
+        } else {
+            List list = new ArrayList();
+            while (iterator.hasNext()) {
+                list.add(iterator.next());
+            }
+            this.iterator = list.iterator();   
+        }
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     */
+    public Enumerator(Map map) {
+
+        this(map.values().iterator());
+
+    }
+
+
+    /**
+     * Return an Enumeration over the values of the specified Map.
+     *
+     * @param map Map whose values should be enumerated
+     * @param clone true to clone iterator
+     */
+    public Enumerator(Map map, boolean clone) {
+
+        this(map.values().iterator(), clone);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Iterator</code> over which the <code>Enumeration</code>
+     * represented by this class actually operates.
+     */
+    private Iterator iterator = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Tests if this enumeration contains more elements.
+     *
+     * @return <code>true</code> if and only if this enumeration object
+     *  contains at least one more element to provide, <code>false</code>
+     *  otherwise
+     */
+    public boolean hasMoreElements() {
+
+        return (iterator.hasNext());
+
+    }
+
+
+    /**
+     * Returns the next element of this enumeration if this enumeration
+     * has at least one more element to provide.
+     *
+     * @return the next element of this enumeration
+     *
+     * @exception NoSuchElementException if no more elements exist
+     */
+    public Object nextElement() throws NoSuchElementException {
+
+        return (iterator.next());
+
+    }
+
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Enumerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,393 @@
+/*
+ * 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.servlets.util;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.TreeMap;
+
+
+/**
+ * Utility class for string parsing that is higher performance than
+ * StringParser for simple delimited text cases.  Parsing is performed
+ * by setting the string, and then using the <code>findXxxx()</code> and
+ * <code>skipXxxx()</code> families of methods to remember significant
+ * offsets.  To retrieve the parsed substrings, call the <code>extract()</code>
+ * method with the appropriate saved offset values.
+ *
+ * @author Craig R. McClanahan
+ */
+public final class LocaleParser {
+
+    public LocaleParser() {
+        this(null);
+    }
+
+    public LocaleParser(String string) {
+        setString(string);
+    }
+
+    public TreeMap parseLocale(String value) {
+      // Store the accumulated languages that have been requested in
+      // a local collection, sorted by the quality value (so we can
+      // add Locales in descending order).  The values will be ArrayLists
+      // containing the corresponding Locales to be added
+      TreeMap locales = new TreeMap();
+
+      // Preprocess the value to remove all whitespace
+      int white = value.indexOf(' ');
+      if (white < 0)
+          white = value.indexOf('\t');
+      if (white >= 0) {
+          StringBuffer sb = new StringBuffer();
+          int len = value.length();
+          for (int i = 0; i < len; i++) {
+              char ch = value.charAt(i);
+              if ((ch != ' ') && (ch != '\t'))
+                  sb.append(ch);
+          }
+          value = sb.toString();
+      }
+
+      LocaleParser parser = this;
+      // Process each comma-delimited language specification
+      parser.setString(value);        // ASSERT: parser is available to us
+      int length = parser.getLength();
+      while (true) {
+
+          // Extract the next comma-delimited entry
+          int start = parser.getIndex();
+          if (start >= length)
+              break;
+          int end = parser.findChar(',');
+          String entry = parser.extract(start, end).trim();
+          parser.advance();   // For the following entry
+
+          // Extract the quality factor for this entry
+          double quality = 1.0;
+          int semi = entry.indexOf(";q=");
+          if (semi >= 0) {
+              try {
+                  quality = Double.parseDouble(entry.substring(semi + 3));
+              } catch (NumberFormatException e) {
+                  quality = 0.0;
+              }
+              entry = entry.substring(0, semi);
+          }
+
+          // Skip entries we are not going to keep track of
+          if (quality < 0.00005)
+              continue;       // Zero (or effectively zero) quality factors
+          if ("*".equals(entry))
+              continue;       // FIXME - "*" entries are not handled
+
+          // Extract the language and country for this entry
+          String language = null;
+          String country = null;
+          String variant = null;
+          int dash = entry.indexOf('-');
+          if (dash < 0) {
+              language = entry;
+              country = "";
+              variant = "";
+          } else {
+              language = entry.substring(0, dash);
+              country = entry.substring(dash + 1);
+              int vDash = country.indexOf('-');
+              if (vDash > 0) {
+                  String cTemp = country.substring(0, vDash);
+                  variant = country.substring(vDash + 1);
+                  country = cTemp;
+              } else {
+                  variant = "";
+              }
+          }
+
+          // Add a new Locale to the list of Locales for this quality level
+          Locale locale = new Locale(language, country, variant);
+          Double key = new Double(-quality);  // Reverse the order
+          ArrayList values = (ArrayList) locales.get(key);
+          if (values == null) {
+              values = new ArrayList();
+              locales.put(key, values);
+          }
+          values.add(locale);
+
+      }
+
+      return locales;
+    }
+    
+    /**
+     * The characters of the current string, as a character array.  Stored
+     * when the string is first specified to speed up access to characters
+     * being compared during parsing.
+     */
+    private char chars[] = null;
+
+
+    /**
+     * The zero-relative index of the current point at which we are
+     * positioned within the string being parsed.  <strong>NOTE</strong>:
+     * the value of this index can be one larger than the index of the last
+     * character of the string (i.e. equal to the string length) if you
+     * parse off the end of the string.  This value is useful for extracting
+     * substrings that include the end of the string.
+     */
+    private int index = 0;
+
+
+    /**
+     * The length of the String we are currently parsing.  Stored when the
+     * string is first specified to avoid repeated recalculations.
+     */
+    private int length = 0;
+
+
+    /**
+     * The String we are currently parsing.
+     */
+    private String string = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the zero-relative index of our current parsing position
+     * within the string being parsed.
+     */
+    public int getIndex() {
+
+        return (this.index);
+
+    }
+
+
+    /**
+     * Return the length of the string we are parsing.
+     */
+    public int getLength() {
+
+        return (this.length);
+
+    }
+
+
+    /**
+     * Return the String we are currently parsing.
+     */
+    public String getString() {
+
+        return (this.string);
+
+    }
+
+
+    /**
+     * Set the String we are currently parsing.  The parser state is also reset
+     * to begin at the start of this string.
+     *
+     * @param string The string to be parsed.
+     */
+    public void setString(String string) {
+
+        this.string = string;
+        if (string != null) {
+            this.length = string.length();
+            chars = this.string.toCharArray();
+        } else {
+            this.length = 0;
+            chars = new char[0];
+        }
+        reset();
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Advance the current parsing position by one, if we are not already
+     * past the end of the string.
+     */
+    public void advance() {
+
+        if (index < length)
+            index++;
+
+    }
+
+
+    /**
+     * Extract and return a substring that starts at the specified position,
+     * and extends to the end of the string being parsed.  If this is not
+     * possible, a zero-length string is returned.
+     *
+     * @param start Starting index, zero relative, inclusive
+     */
+    public String extract(int start) {
+
+        if ((start < 0) || (start >= length))
+            return ("");
+        else
+            return (string.substring(start));
+
+    }
+
+
+    /**
+     * Extract and return a substring that starts at the specified position,
+     * and ends at the character before the specified position.  If this is
+     * not possible, a zero-length string is returned.
+     *
+     * @param start Starting index, zero relative, inclusive
+     * @param end Ending index, zero relative, exclusive
+     */
+    public String extract(int start, int end) {
+
+        if ((start < 0) || (start >= end) || (end > length))
+            return ("");
+        else
+            return (string.substring(start, end));
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of the specified character,
+     * or the index of the character after the last position of the string
+     * if no more occurrences of this character are found.  The current
+     * parsing position is updated to the returned value.
+     *
+     * @param ch Character to be found
+     */
+    public int findChar(char ch) {
+
+        while ((index < length) && (ch != chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of a non-whitespace character,
+     * or the index of the character after the last position of the string
+     * if no more non-whitespace characters are found.  The current
+     * parsing position is updated to the returned value.
+     */
+    public int findText() {
+
+        while ((index < length) && isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Return the index of the next occurrence of a whitespace character,
+     * or the index of the character after the last position of the string
+     * if no more whitespace characters are found.  The current parsing
+     * position is updated to the returned value.
+     */
+    public int findWhite() {
+
+        while ((index < length) && !isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Reset the current state of the parser to the beginning of the
+     * current string being parsed.
+     */
+    public void reset() {
+
+        index = 0;
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at the
+     * specified character, or until it moves past the end of the string.
+     * Return the final value.
+     *
+     * @param ch Character to be skipped
+     */
+    public int skipChar(char ch) {
+
+        while ((index < length) && (ch == chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at a
+     * non-whitespace character, or until it moves past the end of the string.
+     * Return the final value.
+     */
+    public int skipText() {
+
+        while ((index < length) && !isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    /**
+     * Advance the current parsing position while it is pointing at a
+     * whitespace character, or until it moves past the end of the string.
+     * Return the final value.
+     */
+    public int skipWhite() {
+
+        while ((index < length) && isWhite(chars[index]))
+            index++;
+        return (index);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Is the specified character considered to be whitespace?
+     *
+     * @param ch Character to be checked
+     */
+    protected boolean isWhite(char ch) {
+
+        if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,146 @@
+/**
+ * 
+ */
+package org.apache.tomcat.servlets.util;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/** 
+ * Utils to process HTTP/1.1 ranges. Used by default servlet, could 
+ * be used by any servlet that needs to deal with ranges.
+ * 
+ * It is very good to support ranges if you have large content. In most
+ * cases supporting one range is enough - getting multiple ranges doesn't
+ * seem very common, and it's complex (multipart response).
+ * 
+ * @author Costin Manolache
+ * @author Remy Maucherat 
+ * @author - see DefaultServlet in Catalin for other contributors
+ */
+public class Range {
+
+    public long start;
+    public long end;
+    public long length;
+
+    /**
+     * Validate range.
+     */
+    public boolean validate() {
+        if (end >= length)
+            end = length - 1;
+        return ( (start >= 0) && (end >= 0) && (start <= end)
+                 && (length > 0) );
+    }
+
+    public void recycle() {
+        start = 0;
+        end = 0;
+        length = 0;
+    }
+    
+    /** Parse ranges. 
+     * 
+     * @return null if the range is invalid or can't be parsed
+     */
+    public static ArrayList parseRanges(long fileLength, 
+                                        String rangeHeader) throws IOException {
+        ArrayList result = new ArrayList();
+        StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
+
+        // Parsing the range list
+        while (commaTokenizer.hasMoreTokens()) {
+            String rangeDefinition = commaTokenizer.nextToken().trim();
+
+            Range currentRange = new Range();
+            currentRange.length = fileLength;
+
+            int dashPos = rangeDefinition.indexOf('-');
+
+            if (dashPos == -1) {
+                return null;
+            }
+
+            if (dashPos == 0) {
+                try {
+                    long offset = Long.parseLong(rangeDefinition);
+                    currentRange.start = fileLength + offset;
+                    currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    return null;
+                }
+            } else {
+
+                try {
+                    currentRange.start = Long.parseLong
+                        (rangeDefinition.substring(0, dashPos));
+                    if (dashPos < rangeDefinition.length() - 1)
+                        currentRange.end = Long.parseLong
+                            (rangeDefinition.substring
+                             (dashPos + 1, rangeDefinition.length()));
+                    else
+                        currentRange.end = fileLength - 1;
+                } catch (NumberFormatException e) {
+                    return null;
+                }
+
+            }
+            if (!currentRange.validate()) {
+                return null;
+            }
+            result.add(currentRange);
+        }
+        return result;
+    }
+
+
+    /**
+     * Parse the Content-Range header. Used with PUT or in response.
+     * 
+     * @return Range
+     */
+    public static Range parseContentRange(String rangeHeader)
+            throws IOException {
+        if (rangeHeader == null)
+            return null;
+
+        // bytes is the only range unit supported
+        if (!rangeHeader.startsWith("bytes")) {
+            return null;
+        }
+
+        rangeHeader = rangeHeader.substring(6).trim();
+
+        int dashPos = rangeHeader.indexOf('-');
+        int slashPos = rangeHeader.indexOf('/');
+
+        if (dashPos == -1) {
+            return null;
+        }
+
+        if (slashPos == -1) {
+            return null;
+        }
+
+        Range range = new Range();
+
+        try {
+            range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
+            range.end =
+                Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
+            range.length = Long.parseLong
+                (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+
+        if (!range.validate()) {
+            return null;
+        }
+
+        return range;
+    }
+
+}
\ No newline at end of file

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,509 @@
+/*
+ * 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.servlets.util;
+
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.servlet.http.Cookie;
+
+
+/**
+ * General purpose request parsing and encoding utility methods.
+ *
+ * @author Craig R. McClanahan
+ * @author Tim Tye
+ * @version $Revision: 302905 $ $Date: 2004-05-26 18:41:54 +0200 (mer., 26 mai 2004) $
+ */
+
+public final class RequestUtil {
+
+
+    /**
+     * The DateFormat to use for generating readable dates in cookies.
+     */
+    private static SimpleDateFormat format =
+        new SimpleDateFormat(" EEEE, dd-MMM-yy kk:mm:ss zz");
+
+    static {
+        format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    }
+
+
+    /**
+     * Encode a cookie as per RFC 2109.  The resulting string can be used
+     * as the value for a <code>Set-Cookie</code> header.
+     *
+     * @param cookie The cookie to encode.
+     * @return A string following RFC 2109.
+     */
+    public static String encodeCookie(Cookie cookie) {
+
+        StringBuffer buf = new StringBuffer( cookie.getName() );
+        buf.append("=");
+        buf.append(cookie.getValue());
+
+        if (cookie.getComment() != null) {
+            buf.append("; Comment=\"");
+            buf.append(cookie.getComment());
+            buf.append("\"");
+        }
+
+        if (cookie.getDomain() != null) {
+            buf.append("; Domain=\"");
+            buf.append(cookie.getDomain());
+            buf.append("\"");
+        }
+
+        long age = cookie.getMaxAge();
+        if (cookie.getMaxAge() >= 0) {
+            buf.append("; Max-Age=\"");
+            buf.append(cookie.getMaxAge());
+            buf.append("\"");
+        }
+
+        if (cookie.getPath() != null) {
+            buf.append("; Path=\"");
+            buf.append(cookie.getPath());
+            buf.append("\"");
+        }
+
+        if (cookie.getSecure()) {
+            buf.append("; Secure");
+        }
+
+        if (cookie.getVersion() > 0) {
+            buf.append("; Version=\"");
+            buf.append(cookie.getVersion());
+            buf.append("\"");
+        }
+
+        return (buf.toString());
+    }
+
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+        if (message == null)
+            return (null);
+
+        char content[] = new char[message.length()];
+        message.getChars(0, message.length(), content, 0);
+        StringBuffer result = new StringBuffer(content.length + 50);
+        for (int i = 0; i < content.length; i++) {
+            switch (content[i]) {
+            case '<':
+                result.append("&lt;");
+                break;
+            case '>':
+                result.append("&gt;");
+                break;
+            case '&':
+                result.append("&amp;");
+                break;
+            case '"':
+                result.append("&quot;");
+                break;
+            default:
+                result.append(content[i]);
+            }
+        }
+        return (result.toString());
+
+    }
+
+
+    /**
+     * Normalize a relative URI path that may have relative values ("/./",
+     * "/../", and so on ) it it.  <strong>WARNING</strong> - This method is
+     * useful only for normalizing application-generated paths.  It does not
+     * try to perform security checks for malicious input.
+     *
+     * @param path Relative path to be normalized
+     */
+    public static String normalize(String path) {
+
+        if (path == null)
+            return null;
+
+        // Create a place for the normalized path
+        String normalized = path;
+
+        if (normalized.equals("/."))
+            return "/";
+
+        // Add a leading "/" if necessary
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                return (null);  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Return the normalized path that we have completed
+        return (normalized);
+
+    }
+
+
+    /**
+     * Parse the character encoding from the specified content type header.
+     * If the content type is null, or there is no explicit character encoding,
+     * <code>null</code> is returned.
+     *
+     * @param contentType a content type header
+     */
+    public static String parseCharacterEncoding(String contentType) {
+
+        if (contentType == null)
+            return (null);
+        int start = contentType.indexOf("charset=");
+        if (start < 0)
+            return (null);
+        String encoding = contentType.substring(start + 8);
+        int end = encoding.indexOf(';');
+        if (end >= 0)
+            encoding = encoding.substring(0, end);
+        encoding = encoding.trim();
+        if ((encoding.length() > 2) && (encoding.startsWith("\""))
+            && (encoding.endsWith("\"")))
+            encoding = encoding.substring(1, encoding.length() - 1);
+        return (encoding.trim());
+
+    }
+
+
+    /**
+     * Parse a cookie header into an array of cookies according to RFC 2109.
+     *
+     * @param header Value of an HTTP "Cookie" header
+     */
+    public static Cookie[] parseCookieHeader(String header) {
+
+        if ((header == null) || (header.length() < 1))
+            return (new Cookie[0]);
+
+        ArrayList cookies = new ArrayList();
+        while (header.length() > 0) {
+            int semicolon = header.indexOf(';');
+            if (semicolon < 0)
+                semicolon = header.length();
+            if (semicolon == 0)
+                break;
+            String token = header.substring(0, semicolon);
+            if (semicolon < header.length())
+                header = header.substring(semicolon + 1);
+            else
+                header = "";
+            try {
+                int equals = token.indexOf('=');
+                if (equals > 0) {
+                    String name = token.substring(0, equals).trim();
+                    String value = token.substring(equals+1).trim();
+                    cookies.add(new Cookie(name, value));
+                }
+            } catch (Throwable e) {
+                ;
+            }
+        }
+
+        return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
+
+    }
+
+
+    /**
+     * Append request parameters from the specified String to the specified
+     * Map.  It is presumed that the specified Map is not accessed from any
+     * other thread, so no synchronization is performed.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
+     * individually on the parsed name and value elements, rather than on
+     * the entire query string ahead of time, to properly deal with the case
+     * where the name or value includes an encoded "=" or "&" character
+     * that would otherwise be interpreted as a delimiter.
+     *
+     * @param map Map that accumulates the resulting parameters
+     * @param data Input string containing request parameters
+     *
+     * @exception IllegalArgumentException if the data is malformed
+     */
+    public static void parseParameters(Map map, String data, String encoding)
+        throws UnsupportedEncodingException {
+
+        if ((data != null) && (data.length() > 0)) {
+
+            // use the specified encoding to extract bytes out of the
+            // given string so that the encoding is not lost. If an
+            // encoding is not specified, let it use platform default
+            byte[] bytes = null;
+            try {
+                if (encoding == null) {
+                    bytes = data.getBytes();
+                } else {
+                    bytes = data.getBytes(encoding);
+                }
+            } catch (UnsupportedEncodingException uee) {
+            }
+
+            parseParameters(map, bytes, encoding);
+        }
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded String.
+     * When the byte array is converted to a string, the system default
+     * character encoding is used...  This may be different than some other
+     * servers.
+     *
+     * @param str The url-encoded string
+     *
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(String str) {
+
+        return URLDecode(str, null);
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded String.
+     *
+     * @param str The url-encoded string
+     * @param enc The encoding to use; if null, the default encoding is used
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(String str, String enc) {
+
+        if (str == null)
+            return (null);
+
+        // use the specified encoding to extract bytes out of the
+        // given string so that the encoding is not lost. If an
+        // encoding is not specified, let it use platform default
+        byte[] bytes = null;
+        try {
+            if (enc == null) {
+                bytes = str.getBytes();
+            } else {
+                bytes = str.getBytes(enc);
+            }
+        } catch (UnsupportedEncodingException uee) {}
+
+        return URLDecode(bytes, enc);
+
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded byte array.
+     *
+     * @param bytes The url-encoded byte array
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(byte[] bytes) {
+        return URLDecode(bytes, null);
+    }
+
+
+    /**
+     * Decode and return the specified URL-encoded byte array.
+     *
+     * @param bytes The url-encoded byte array
+     * @param enc The encoding to use; if null, the default encoding is used
+     * @exception IllegalArgumentException if a '%' character is not followed
+     * by a valid 2-digit hexadecimal number
+     */
+    public static String URLDecode(byte[] bytes, String enc) {
+
+        if (bytes == null)
+            return (null);
+
+        int len = bytes.length;
+        int ix = 0;
+        int ox = 0;
+        while (ix < len) {
+            byte b = bytes[ix++];     // Get byte to test
+            if (b == '+') {
+                b = (byte)' ';
+            } else if (b == '%') {
+                b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
+                            + convertHexDigit(bytes[ix++]));
+            }
+            bytes[ox++] = b;
+        }
+        if (enc != null) {
+            try {
+                return new String(bytes, 0, ox, enc);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return new String(bytes, 0, ox);
+
+    }
+
+
+    /**
+     * Convert a byte character value to hexidecimal digit value.
+     *
+     * @param b the character value byte
+     */
+    private static byte convertHexDigit( byte b ) {
+        if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
+        if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
+        if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
+        return 0;
+    }
+
+
+    /**
+     * Put name and value pair in map.  When name already exist, add value
+     * to array of values.
+     *
+     * @param map The map to populate
+     * @param name The parameter name
+     * @param value The parameter value
+     */
+    private static void putMapEntry( Map map, String name, String value) {
+        String[] newValues = null;
+        String[] oldValues = (String[]) map.get(name);
+        if (oldValues == null) {
+            newValues = new String[1];
+            newValues[0] = value;
+        } else {
+            newValues = new String[oldValues.length + 1];
+            System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
+            newValues[oldValues.length] = value;
+        }
+        map.put(name, newValues);
+    }
+
+
+    /**
+     * Append request parameters from the specified String to the specified
+     * Map.  It is presumed that the specified Map is not accessed from any
+     * other thread, so no synchronization is performed.
+     * <p>
+     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
+     * individually on the parsed name and value elements, rather than on
+     * the entire query string ahead of time, to properly deal with the case
+     * where the name or value includes an encoded "=" or "&" character
+     * that would otherwise be interpreted as a delimiter.
+     *
+     * NOTE: byte array data is modified by this method.  Caller beware.
+     *
+     * @param map Map that accumulates the resulting parameters
+     * @param data Input string containing request parameters
+     * @param encoding Encoding to use for converting hex
+     *
+     * @exception UnsupportedEncodingException if the data is malformed
+     */
+    public static void parseParameters(Map map, byte[] data, String encoding)
+        throws UnsupportedEncodingException {
+
+        if (data != null && data.length > 0) {
+            int    pos = 0;
+            int    ix = 0;
+            int    ox = 0;
+            String key = null;
+            String value = null;
+            while (ix < data.length) {
+                byte c = data[ix++];
+                switch ((char) c) {
+                case '&':
+                    value = new String(data, 0, ox, encoding);
+                    if (key != null) {
+                        putMapEntry(map, key, value);
+                        key = null;
+                    }
+                    ox = 0;
+                    break;
+                case '=':
+                    if (key == null) {
+                        key = new String(data, 0, ox, encoding);
+                        ox = 0;
+                    } else {
+                        data[ox++] = c;
+                    }                   
+                    break;  
+                case '+':
+                    data[ox++] = (byte)' ';
+                    break;
+                case '%':
+                    data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
+                                    + convertHexDigit(data[ix++]));
+                    break;
+                default:
+                    data[ox++] = c;
+                }
+            }
+            //The last value does not end in '&'.  So save it now.
+            if (key != null) {
+                value = new String(data, 0, ox, encoding);
+                putMapEntry(map, key, value);
+            }
+        }
+
+    }
+
+
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java?rev=761964&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java Sat Apr  4 16:24:34 2009
@@ -0,0 +1,67 @@
+package org.apache.tomcat.servlets.util;
+
+public class UrlUtils {
+
+    /** Used by webdav.
+     * 
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     *
+     * @param path Path to be normalized
+     */
+    public static String normalize(String path) {
+
+        if (path == null)
+            return null;
+
+        // Create a place for the normalized path
+        String normalized = path;
+
+        if (normalized.equals("/."))
+            return "/";
+
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+        
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                return (null);  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Return the normalized path that we have completed
+        return (normalized);
+    }
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native



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


Mime
View raw message