Moved utils to connector ( the servlet part will either go away or be a separate...
authorcostin <costin@13f79535-47bb-0310-9956-ffa450edef68>
Wed, 24 Feb 2010 18:03:00 +0000 (18:03 +0000)
committercostin <costin@13f79535-47bb-0310-9956-ffa450edef68>
Wed, 24 Feb 2010 18:03:00 +0000 (18:03 +0000)
issues found while load testing. SSL fixes. Separate build target for connector.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@915902 13f79535-47bb-0310-9956-ffa450edef68

53 files changed:
modules/tomcat-lite/build.xml
modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/JmxObjectManagerSpi.java
modules/tomcat-lite/java/org/apache/tomcat/integration/jmx/UJmxHandler.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/ContentType.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/Http11Connection.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnectionPool.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpConnector.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpMessage.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpRequest.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/HttpResponse.java
modules/tomcat-lite/java/org/apache/tomcat/lite/http/SpdyConnection.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOBuffer.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOChannel.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOConnector.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOWriter.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioChannel.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioThread.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketIOChannel.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslChannel.java
modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslConnector.java
modules/tomcat-lite/java/org/apache/tomcat/lite/servlet/ServletApi30.java
modules/tomcat-lite/java/org/apache/tomcat/lite/servlet/ServletContextImpl.java
modules/tomcat-lite/java/org/apache/tomcat/lite/servlet/ServletRequestImpl.java
modules/tomcat-lite/java/org/apache/tomcat/lite/servlet/WebappFilterMapper.java
modules/tomcat-lite/java/org/apache/tomcat/lite/util/Base64.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/util/FastHttpDateFormat.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/util/LocaleParser.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/util/MimeMap.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/util/Range.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/util/URLEncoder.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/lite/util/UrlUtils.java [new file with mode: 0644]
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/DefaultServlet.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/file/WebdavServlet.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Base64.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/FastHttpDateFormat.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/MimeMap.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/RequestUtil.java
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/URLEncoder.java [deleted file]
modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java [deleted file]
modules/tomcat-lite/test/org/apache/coyote/lite/TomcatLiteCoyoteTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/TestMain.java
modules/tomcat-lite/test/org/apache/tomcat/lite/http/HttpsTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/http/LiveHttp1Test.java
modules/tomcat-lite/test/org/apache/tomcat/lite/http/SpdyTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/load/LiveHttpThreadedTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/load/MicroTest.java
modules/tomcat-lite/test/org/apache/tomcat/lite/load/ThreadRunner.java
modules/tomcat-lite/test/org/apache/tomcat/lite/test.properties
modules/tomcat-lite/test/org/apache/tomcat/lite/util/UEncoderTest.java [new file with mode: 0644]
modules/tomcat-lite/test/org/apache/tomcat/util/buf/UEncoderTest.java [deleted file]

index 36974b0..9941f37 100644 (file)
     <property name="tomcat.lite.src" value="${basedir}/" />
     <property name="classes" value="${basedir}/target/tomcat-lite/classes" />
     <property name="test-classes" value="${basedir}/target/tomcat-lite/test-classes" />
+    <property name="http.test-classes" value="${basedir}/target/tomcat-lite/http-test-classes" />
     <property name="jar.dir" value="${basedir}/target/tomcat-lite/" />
     <property name="MAIN" value="org.apache.tomcat.integration.simple.Main"/>
     <property name="compile.source" value="1.6"/>
+    <property name="compile.debug" value="true"/>
  
     <!-- All Ivy downloads -->
     <path id='lite-classpath'>
         <pathelement location="target/lib/commons-codec.jar"/>
     </path>
 
+    <target name="http" depends="http.compile,http.test,http.pack"/>
+    
+    <target name="http.compile" 
+           description="Compile only the HTTP connector classes, no servlets">
+        <mkdir dir="${classes}"/>
+        <javac destdir="${classes}"
+               debug="${compile.debug}"
+               deprecation="${compile.deprecation}"
+               source="${compile.source}"
+               optimize="${compile.optimize}"
+               encoding="ISO-8859-1">
+            <classpath refid="lite-classpath" />
+            <src path="${tomcat.lite.src}/java" />
+            <classpath refid="head-classpath" />
+            <exclude name="org/apache/tomcat/servlets/**"/>
+            <exclude name="org/apache/tomcat/lite/servlet/**"/>
+        </javac>
+        <copy todir="${classes}">
+            <fileset dir="${tomcat.lite.src}/java" 
+                includes="**/*.properties **/*.xml" />
+        </copy>
+    </target>
+     
+    <target name="http.test.compile" 
+           description="Test only the HTTP connector classes, no servlets">
+        <mkdir dir="${http.test-classes}"/>
+        <javac destdir="${http.test-classes}"
+                debug="${compile.debug}"
+                deprecation="${compile.deprecation}"
+                source="${compile.source}"
+                optimize="${compile.optimize}"
+                encoding="ISO-8859-1"
+         >
+             <classpath refid="lite-classpath" />
+             <classpath refid="head-classpath" />
+             <classpath path="${classes}" />
+             <src path="${tomcat.lite.src}/test" />
+            <exclude name="org/apache/tomcat/lite/servlet/**"/>
+            <exclude name="org/apache/coyote/lite/**"/>
+            <exclude name="org/apache/tomcat/test/watchdog/**"/>
+         </javac>
+        <copy todir="${http.test-classes}">
+            <fileset dir="${tomcat.lite.src}/test" 
+                includes="**/*.properties **/*.xml  **/*.bin **/*.keystore" />
+        </copy>
+   </target>
+        
+      <target name="http.test" depends="http.test.compile" >
+          <!-- also: perTest(default) -->
+          <junit printsummary="on" fork="once"
+                    timeout="600000" maxmemory="1G" outputtoformatters="no"
+                    >
+              <classpath refid="lite-classpath" />
+              <classpath refid="head-classpath" />
+              <classpath path="${http.test-classes}" />
+              <classpath path="${classes}" />
+
+            <formatter type="brief" usefile="false" />
+
+            <batchtest>
+              <fileset dir="test" >
+                 <!-- Include all by default -->
+                  <include name="**/*Test.java" />
+                  <include name="**/*Tests.java" />
+                 <!-- Exclude TestAll ortherwise there will be duplicated -->
+                 <exclude name="**/TestAll.java" />
+                 <!-- Exclude helper classes -->
+                 <exclude name="**/Tester*.java" />
+
+                     <exclude name="org/apache/tomcat/lite/servlet/**"/>
+                     <exclude name="org/apache/coyote/lite/**"/>
+                     <exclude name="org/apache/tomcat/test/watchdog/**"/>
+              </fileset>
+             </batchtest>
+              </junit>        
+    </target>
+    
+    <target name="runtest" depends="http.test.compile">
+        <junit printsummary="withOutAndErr" fork="no" dir="${tomcat.base}">
+            <sysproperty key="tests" value="${tests}"/>
+            <classpath refid="lite-classpath" />
+            <classpath refid="head-classpath" />
+            <classpath path="${http.test-classes}" />
+            <classpath path="${classes}" />
+
+            <formatter type="plain" usefile="false" />
+
+          <batchtest>
+            <fileset dir="test" >
+                <include name="**/${test}.java" />
+            </fileset>
+           </batchtest>
+            </junit>        
+        
+     </target>
+    
+    <target name="http.pack" 
+           description="Pack the HTTP client and connector">
+    </target>
      
     <target name="compile" 
-           description="Build against tomcat head">
+           description="Build all classes against tomcat head.">
+        <mkdir dir="${classes}"/>
         <javac destdir="${classes}"
                debug="${compile.debug}"
                deprecation="${compile.deprecation}"
                deprecation="${compile.deprecation}"
                source="${compile.source}"
                optimize="${compile.optimize}"
-               encoding="ISO-8859-1"
-        >
+               encoding="ISO-8859-1">
             <classpath refid="lite-classpath" />
             <src path="${tomcat.lite.src}/java" />
             <exclude name="**/ServletApi30.java"/>
             <exclude name="org/apache/tomcat/coyote/servlet/*.java"/>
         </javac>
-
         <copy todir="${classes}">
             <fileset dir="${tomcat.lite.src}/java" 
                 includes="**/*.properties **/*.xml" />
index 45bec43..02b4490 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * 
  * All objects of interest are registered automatically.
  */
-public class JmxObjectManagerSpi extends ObjectManager {
+public class JmxObjectManagerSpi extends ObjectManager implements Runnable {
     Registry registry;
     Logger log = Logger.getLogger("JmxObjectManager");
     
@@ -54,5 +54,15 @@ public class JmxObjectManagerSpi extends ObjectManager {
     public Object get(String key) {
         return null;
     }
+    
+    ObjectManager om;
+    
+    public void setObjectManager(ObjectManager om) {
+        this.om = om;
+    }
 
+    public void run() {
+        om.register(this);
+        // TODO: register existing objects in JMX
+    }
 }
index c5a2e76..660131a 100644 (file)
@@ -21,14 +21,11 @@ package org.apache.tomcat.integration.jmx;
 
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
 import java.util.logging.Logger;
 
 import org.apache.tomcat.integration.DynamicObject;
-import org.apache.tomcat.integration.ObjectManager;
 import org.apache.tomcat.lite.http.HttpRequest;
 import org.apache.tomcat.lite.http.HttpResponse;
 import org.apache.tomcat.lite.http.HttpWriter;
@@ -47,16 +44,18 @@ import org.apache.tomcat.lite.http.HttpChannel.HttpService;
  */
 public class UJmxHandler implements HttpService {
 
-    private static Logger log = Logger.getLogger(UJmxHandler.class.getName());
+    protected static Logger log = Logger.getLogger(UJmxHandler.class.getName());
     private UJmxObjectManagerSpi jmx;
     
+    public UJmxHandler() {
+    }
+    
     public UJmxHandler(UJmxObjectManagerSpi jmx) {
         this.jmx = jmx;
     }
     
     public void getAttribute(PrintWriter writer, String onameStr, String att) {
         try {
-            
             Object bean = jmx.objects.get(onameStr);
             Class beanClass = bean.getClass();
             DynamicObject ci = jmx.getClassInfo(beanClass);
index 993566c..c98276c 100644 (file)
@@ -72,7 +72,7 @@ public class ContentType {
         int index = type.indexOf(';');
         while (index != -1) {
             index++;
-            while (index < len && Character.isSpace(type.charAt(index))) {
+            while (index < len && Character.isWhitespace(type.charAt(index))) {
                 index++;
             }
             if (index+8 < len
index 2571a85..9529a19 100644 (file)
@@ -4,6 +4,8 @@ package org.apache.tomcat.lite.http;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -144,6 +146,8 @@ public class Http11Connection extends HttpConnection
                 return closeInHead();
             }
         }
+        
+        
         return true;
     }
     
@@ -209,6 +213,11 @@ public class Http11Connection extends HttpConnection
             if (!headersReceived) {
                 headRecvBuf.wrapTo(headW);
                 parseMessage(activeHttp, headW);
+                // Part of parseMessage we can switch the protocol
+                if (switchedProtocol != null) {
+                    return;
+                }
+                
                 if (serverMode && activeHttp.httpReq.decodedUri.remaining() == 0) {
                     abort(activeHttp, "Invalid url");
                 }
@@ -552,6 +561,8 @@ public class Http11Connection extends HttpConnection
         return statusCode.remaining() > 0;
     }
 
+    List<String> connectionHeaders = new ArrayList<String>();
+    
     private void parseHeaders(HttpChannel http, HttpMessageBytes msgBytes,
             BBuffer head) 
                 throws IOException {
@@ -559,6 +570,9 @@ public class Http11Connection extends HttpConnection
         head.readLine(line);
         
         int idx = 0;
+        
+        BBuffer upgrade = null;
+        
         while(line.remaining() > 0) {
             // not empty..
             idx = msgBytes.addHeader();
@@ -567,7 +581,23 @@ public class Http11Connection extends HttpConnection
             parseHeader(http, head, line, nameBuf, valBuf);
             
             // TODO: process 'interesting' headers here.
+            if (nameBuf.equalsIgnoreCase("connection")) {
+                // TODO: save and remove if not recognized
+            }
+            if (nameBuf.equalsIgnoreCase("upgrade")) {
+                upgrade = valBuf;
+            }
+        }
+        
+        if (upgrade != null) {
+            if (upgrade.equalsIgnoreCase("WebSocket")) {
+                
+            } else if (upgrade.equalsIgnoreCase("SPDY/1.0")) {
+                
+            }
         }
+        
+        // TODO: process connection headers 
     }
 
     /**
@@ -1406,8 +1436,10 @@ public class Http11Connection extends HttpConnection
         HttpChannel httpCh = activeHttp; 
         boolean ssl = httpCh.getRequest().isSecure();
         if (ssl) {
-            SslChannel ch1 = new SslChannel();
-            ch1.setSslContext(httpConnector.sslConnector.getSSLContext());
+            String[] hostPort = httpCh.getTarget().split(":");
+            
+            SslChannel ch1 = httpConnector.sslConnector.channel(
+                    hostPort[0], Integer.parseInt(hostPort[1]));
             ch1.setSink(net);
             net.addFilterAfter(ch1);
             net = ch1;
@@ -1419,7 +1451,7 @@ public class Http11Connection extends HttpConnection
         }
 
         if (!net.isOpen()) {
-            httpCh.abort("Can't connect");
+            httpCh.abort(net.lastException());
             return;
         }
         
index 363ac59..696a470 100644 (file)
@@ -282,14 +282,14 @@ public class HttpConnectionPool {
 
         if (con.isOpen()) {
             hits.incrementAndGet();
-            if (debug) {
-                httpCh.trace("HTTP_CONNECT: Reuse connection " + target + " " + this);
-            }
+//            if (debug) {
+//                log.info("HTTP_CONNECT: Reuse connection " + target + " " + this);
+//            }
             con.sendRequest(httpCh);
         } else {
             misses.incrementAndGet();
             if (debug) {
-                httpCh.trace("HTTP_CONNECT: Start connection " + target + " " + this);
+                log.info("HTTP_CONNECT: Start connection " + target + " " + this);
             }
             httpConnect(httpCh, target, ssl, 
                     (Http11Connection) con);
@@ -300,7 +300,7 @@ public class HttpConnectionPool {
             boolean ssl, IOConnector.ConnectedCallback cb)
             throws IOException {
         if (debug) {
-            httpCh.trace("HTTP_CONNECT: New connection " + target);
+            log.info("HTTP_CONNECT: New connection " + target);
         }
         String[] hostPort = target.split(":");
 
@@ -341,9 +341,6 @@ public class HttpConnectionPool {
             // again.
             if (remoteServer.pending.size() == 0) {
                 con.activeHttp = null;
-                if (debug) {
-                    log.info("After request: no pending");
-                }
                 return;
             }
             req = remoteServer.pending.remove();
index 63a6572..cc272e9 100644 (file)
@@ -115,6 +115,10 @@ public class HttpConnector {
         return dispatcher;
     }
     
+    public HttpConnectionPool getConnectionPool() {
+        return cpool;
+    }
+    
     public HttpConnector withIOConnector(IOConnector selectors) {
         ioConnector = selectors;
         return this;
index 867ca2e..3bd8f01 100644 (file)
@@ -4,16 +4,23 @@ package org.apache.tomcat.lite.http;
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
 
 import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
 import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
 import org.apache.tomcat.lite.io.BBuffer;
 import org.apache.tomcat.lite.io.BufferedIOReader;
 import org.apache.tomcat.lite.io.CBuffer;
+import org.apache.tomcat.lite.io.FastHttpDateFormat;
 import org.apache.tomcat.lite.io.IOBuffer;
 import org.apache.tomcat.lite.io.IOInputStream;
 import org.apache.tomcat.lite.io.IOOutputStream;
@@ -113,7 +120,9 @@ public abstract class HttpMessage {
             }
         }
     }
-    
+
+    protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
     private HttpMessageBytes msgBytes = new HttpMessageBytes();
     
     protected HttpMessage.State state = HttpMessage.State.HEAD;
@@ -149,6 +158,15 @@ public abstract class HttpMessage {
     long contentLength = -2;
     boolean chunked;
     
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     *
+     * Notice that because SimpleDateFormat is not thread-safe, we can't
+     * declare formats[] as a static variable.
+     */
+    protected SimpleDateFormat formats[] = null;
+
+    
     BBuffer clBuffer = BBuffer.allocate(64);
     
     public HttpMessage(HttpChannel httpCh) {
@@ -189,6 +207,41 @@ public abstract class HttpMessage {
         return headers;
     }
     
+    /**
+     * Return the value of the specified date header, if any; otherwise
+     * return -1.
+     *
+     * @param name Name of the requested date header
+     *
+     * @exception IllegalArgumentException if the specified header value
+     *  cannot be converted to a date
+     */
+    public long getDateHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null)
+            return (-1L);
+        if (formats == null) {
+            formats = new SimpleDateFormat[] {
+                new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+                new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+                new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+            };
+            formats[0].setTimeZone(GMT_ZONE);
+            formats[1].setTimeZone(GMT_ZONE);
+            formats[2].setTimeZone(GMT_ZONE);
+        }
+        
+        // Attempt to convert the date header in a variety of formats
+        long result = FastHttpDateFormat.parseDate(value, formats);
+        if (result != (-1L)) {
+            return result;
+        }
+        throw new IllegalArgumentException(value);
+
+    }
+
+    
     public Collection<String> getHeaderNames() {
 
         MultiMap headers = getMimeHeaders();
@@ -373,6 +426,14 @@ public abstract class HttpMessage {
         return in;
     }
     
+    public InputStream getInputStream() {
+        return in;
+    }
+    
+    public IOOutputStream getOutputStream() {
+        return out;
+    }
+    
     public IOOutputStream getBodyOutputStream() {
         return out;
     }
@@ -410,6 +471,10 @@ public abstract class HttpMessage {
         reader.setEncoding(getCharacterEncoding());
         return bufferedReader;
     }
+
+    public PrintWriter getWriter() {
+        return new PrintWriter(getBodyWriter());
+    }
     
     public HttpWriter getBodyWriter() {
         conv.setEncoding(getCharacterEncoding());
index bbc6707..c435946 100644 (file)
@@ -175,6 +175,30 @@ public class HttpRequest extends HttpMessage {
         return (mappingData);
     }
     
+    /**
+     * Return the portion of the request URI used to select the Context
+     * of the Request.
+     */
+    public String getContextPath() {
+        return (getMappingData().contextPath.toString());
+    }
+
+    public String getPathInfo() {
+        CBuffer pathInfo = getMappingData().pathInfo;
+        if (pathInfo.length() == 0) {
+            return null;
+        }
+        return (getMappingData().pathInfo.toString());
+    }
+
+    /**
+     * Return the portion of the request URI used to select the servlet
+     * that will process this request.
+     */
+    public String getServletPath() {
+        return (getMappingData().wrapperPath.toString());
+    }
+    
     /** 
      * Parse query parameters - but not POST body. 
      * 
@@ -843,6 +867,9 @@ public class HttpRequest extends HttpMessage {
      */
     protected void processReceivedHeaders() throws IOException {
         BBuffer url = getMsgBytes().url();
+        if (url.remaining() == 0) {
+            System.err.println("No input");
+        }
         if (url.get(0) == 'h') {
             int firstSlash = url.indexOf('/', 0);
             schemeMB.appendAscii(url.array(), 
index fd38abf..39f2ffa 100644 (file)
@@ -8,9 +8,336 @@ import java.util.HashMap;
 import org.apache.tomcat.lite.io.BBucket;
 import org.apache.tomcat.lite.io.BBuffer;
 import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.IOBuffer;
 
 public class HttpResponse extends HttpMessage {
+    
+    /*
+     * Server status codes; see RFC 2068.
+     */
+
+    /**
+     * Status code (100) indicating the client can continue.
+     */
+
+    public static final int SC_CONTINUE = 100;
+
+    
+    /**
+     * Status code (101) indicating the server is switching protocols
+     * according to Upgrade header.
+     */
+
+    public static final int SC_SWITCHING_PROTOCOLS = 101;
+
+    /**
+     * Status code (200) indicating the request succeeded normally.
+     */
+
+    public static final int SC_OK = 200;
+
+    /**
+     * Status code (201) indicating the request succeeded and created
+     * a new resource on the server.
+     */
+
+    public static final int SC_CREATED = 201;
+
+    /**
+     * Status code (202) indicating that a request was accepted for
+     * processing, but was not completed.
+     */
+
+    public static final int SC_ACCEPTED = 202;
+
+    /**
+     * Status code (203) indicating that the meta information presented
+     * by the client did not originate from the server.
+     */
+
+    public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+
+    /**
+     * Status code (204) indicating that the request succeeded but that
+     * there was no new information to return.
+     */
+
+    public static final int SC_NO_CONTENT = 204;
+
+    /**
+     * Status code (205) indicating that the agent <em>SHOULD</em> reset
+     * the document view which caused the request to be sent.
+     */
+
+    public static final int SC_RESET_CONTENT = 205;
+
+    /**
+     * Status code (206) indicating that the server has fulfilled
+     * the partial GET request for the resource.
+     */
+
+    public static final int SC_PARTIAL_CONTENT = 206;
+    
+    /**
+     * Used by Webdav.
+     */
+    public static final int SC_MULTI_STATUS = 207;
+    // This one collides with HTTP 1.1
+    // "207 Partial Update OK"
+    
+    /**
+     * Status code (300) indicating that the requested resource
+     * corresponds to any one of a set of representations, each with
+     * its own specific location.
+     */
+
+    public static final int SC_MULTIPLE_CHOICES = 300;
+
+    /**
+     * Status code (301) indicating that the resource has permanently
+     * moved to a new location, and that future references should use a
+     * new URI with their requests.
+     */
+
+    public static final int SC_MOVED_PERMANENTLY = 301;
+
+    /**
+     * Status code (302) indicating that the resource has temporarily
+     * moved to another location, but that future references should
+     * still use the original URI to access the resource.
+     *
+     * This definition is being retained for backwards compatibility.
+     * SC_FOUND is now the preferred definition.
+     */
+
+    public static final int SC_MOVED_TEMPORARILY = 302;
+
+    /**
+    * Status code (302) indicating that the resource reside
+    * temporarily under a different URI. Since the redirection might
+    * be altered on occasion, the client should continue to use the
+    * Request-URI for future requests.(HTTP/1.1) To represent the
+    * status code (302), it is recommended to use this variable.
+    */
+
+    public static final int SC_FOUND = 302;
+
+    /**
+     * Status code (303) indicating that the response to the request
+     * can be found under a different URI.
+     */
+
+    public static final int SC_SEE_OTHER = 303;
+
+    /**
+     * Status code (304) indicating that a conditional GET operation
+     * found that the resource was available and not modified.
+     */
+
+    public static final int SC_NOT_MODIFIED = 304;
+
+    /**
+     * Status code (305) indicating that the requested resource
+     * <em>MUST</em> be accessed through the proxy given by the
+     * <code><em>Location</em></code> field.
+     */
+
+    public static final int SC_USE_PROXY = 305;
+
+     /**
+     * Status code (307) indicating that the requested resource 
+     * resides temporarily under a different URI. The temporary URI
+     * <em>SHOULD</em> be given by the <code><em>Location</em></code> 
+     * field in the response.
+     */
+
+     public static final int SC_TEMPORARY_REDIRECT = 307;
+
+    /**
+     * Status code (400) indicating the request sent by the client was
+     * syntactically incorrect.
+     */
+
+    public static final int SC_BAD_REQUEST = 400;
+
+    /**
+     * Status code (401) indicating that the request requires HTTP
+     * authentication.
+     */
+
+    public static final int SC_UNAUTHORIZED = 401;
+
+    /**
+     * Status code (402) reserved for future use.
+     */
+
+    public static final int SC_PAYMENT_REQUIRED = 402;
+
+    /**
+     * Status code (403) indicating the server understood the request
+     * but refused to fulfill it.
+     */
+
+    public static final int SC_FORBIDDEN = 403;
+
+    /**
+     * Status code (404) indicating that the requested resource is not
+     * available.
+     */
+
+    public static final int SC_NOT_FOUND = 404;
+
+    /**
+     * Status code (405) indicating that the method specified in the
+     * <code><em>Request-Line</em></code> is not allowed for the resource
+     * identified by the <code><em>Request-URI</em></code>.
+     */
+
+    public static final int SC_METHOD_NOT_ALLOWED = 405;
+
+    /**
+     * Status code (406) indicating that the resource identified by the
+     * request is only capable of generating response entities which have
+     * content characteristics not acceptable according to the accept
+     * headers sent in the request.
+     */
+
+    public static final int SC_NOT_ACCEPTABLE = 406;
+
+    /**
+     * Status code (407) indicating that the client <em>MUST</em> first
+     * authenticate itself with the proxy.
+     */
+
+    public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+
+    /**
+     * Status code (408) indicating that the client did not produce a
+     * request within the time that the server was prepared to wait.
+     */
+
+    public static final int SC_REQUEST_TIMEOUT = 408;
+
+    /**
+     * Status code (409) indicating that the request could not be
+     * completed due to a conflict with the current state of the
+     * resource.
+     */
+
+    public static final int SC_CONFLICT = 409;
+
+    /**
+     * Status code (410) indicating that the resource is no longer
+     * available at the server and no forwarding address is known.
+     * This condition <em>SHOULD</em> be considered permanent.
+     */
+
+    public static final int SC_GONE = 410;
+
+    /**
+     * Status code (411) indicating that the request cannot be handled
+     * without a defined <code><em>Content-Length</em></code>.
+     */
+
+    public static final int SC_LENGTH_REQUIRED = 411;
+
+    /**
+     * Status code (412) indicating that the precondition given in one
+     * or more of the request-header fields evaluated to false when it
+     * was tested on the server.
+     */
+
+    public static final int SC_PRECONDITION_FAILED = 412;
+
+    /**
+     * Status code (413) indicating that the server is refusing to process
+     * the request because the request entity is larger than the server is
+     * willing or able to process.
+     */
+
+    public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
+
+    /**
+     * Status code (414) indicating that the server is refusing to service
+     * the request because the <code><em>Request-URI</em></code> is longer
+     * than the server is willing to interpret.
+     */
+
+    public static final int SC_REQUEST_URI_TOO_LONG = 414;
+
+    /**
+     * Status code (415) indicating that the server is refusing to service
+     * the request because the entity of the request is in a format not
+     * supported by the requested resource for the requested method.
+     */
+
+    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+
+    /**
+     * Status code (416) indicating that the server cannot serve the
+     * requested byte range.
+     */
+
+    public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+
+    /**
+     * Status code (417) indicating that the server could not meet the
+     * expectation given in the Expect request header.
+     */
+
+    public static final int SC_EXPECTATION_FAILED = 417;
+
+    /**
+     * Status code (423) indicating the destination resource of a
+     * method is locked, and either the request did not contain a
+     * valid Lock-Info header, or the Lock-Info header identifies
+     * a lock held by another principal.
+     */
+    public static final int SC_LOCKED = 423;
+
+    /**
+     * Status code (500) indicating an error inside the HTTP server
+     * which prevented it from fulfilling the request.
+     */
+
+    public static final int SC_INTERNAL_SERVER_ERROR = 500;
+
+    /**
+     * Status code (501) indicating the HTTP server does not support
+     * the functionality needed to fulfill the request.
+     */
+
+    public static final int SC_NOT_IMPLEMENTED = 501;
+
+    /**
+     * Status code (502) indicating that the HTTP server received an
+     * invalid response from a server it consulted when acting as a
+     * proxy or gateway.
+     */
+
+    public static final int SC_BAD_GATEWAY = 502;
+
+    /**
+     * Status code (503) indicating that the HTTP server is
+     * temporarily overloaded, and unable to handle the request.
+     */
+
+    public static final int SC_SERVICE_UNAVAILABLE = 503;
+
+    /**
+     * Status code (504) indicating that the server did not receive
+     * a timely response from the upstream server while acting as
+     * a gateway or proxy.
+     */
+
+    public static final int SC_GATEWAY_TIMEOUT = 504;
+
+    /**
+     * Status code (505) indicating that the server does not support
+     * or refuses to support the HTTP protocol version that was used
+     * in the request message.
+     */
+
+    public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
 
     // will not be recycled
     public Object nativeResponse;
@@ -47,6 +374,14 @@ public class HttpResponse extends HttpMessage {
         status = i;
     }
     
+    public void sendError(int status) {
+        this.status = status;
+    }
+    
+    public void sendError(int status, String msg) {
+        message.set(msg);
+    }
+    
     public int getStatus() {
         if (status >= 0) {
             return status;
@@ -112,7 +447,7 @@ public class HttpResponse extends HttpMessage {
      *  Common messages are cached.
      *
      */
-    BBucket getMessage( int status ) {
+    static BBucket getMessage( int status ) {
         // method from Response.
 
         // Does HTTP requires/allow international messages or
@@ -134,6 +469,9 @@ public class HttpResponse extends HttpMessage {
         return bb;
     }
     
+    public static String getStatusText(int code) {
+        return getMessage(code).toString();
+    }
     
     static BBucket st_unknown = BBuffer.wrapper("No Message");
     static BBucket st_200 = BBuffer.wrapper("OK");
@@ -192,6 +530,8 @@ public class HttpResponse extends HttpMessage {
         addStatus(504, "Gateway Timeout");
         addStatus(505, "HTTP Version Not Supported");
         addStatus(507, "Insufficient Storage");
+        addStatus(SC_LOCKED, "Locked");
+
         
     }
     
index e4c2a77..ccba678 100644 (file)
@@ -95,9 +95,12 @@ public class SpdyConnection extends HttpConnector.HttpConnection
     
     CompressFilter headCompressIn = new CompressFilter()
         .setDictionary(SPDY_DICT, DICT_ID);
+    
     CompressFilter headCompressOut = new CompressFilter()
         .setDictionary(SPDY_DICT, DICT_ID);
+    
     IOBuffer headerCompressBuffer = new IOBuffer();
+    IOBuffer headerDeCompressBuffer = new IOBuffer();
 
     AtomicInteger inFrames = new AtomicInteger();
     AtomicInteger inDataFrames = new AtomicInteger();
@@ -120,32 +123,35 @@ public class SpdyConnection extends HttpConnector.HttpConnection
     }
     
     @Override
-    public synchronized void dataReceived(IOBuffer iob) throws IOException {
-        while (true) {
-            int avail = iob.available();
-            if (avail == 0) {
-                return;
-            }
-            if (currentInFrame == null) {
-                if (inFrameBuffer.remaining() + avail < 8) {
+    public void dataReceived(IOBuffer iob) throws IOException {
+        // Only one thread doing receive at a time.
+        synchronized (inFrameBuffer) {
+            while (true) {
+                int avail = iob.available();
+                if (avail == 0) {
                     return;
                 }
-                if (inFrameBuffer.remaining() < 8) {
-                    int headRest = 8 - inFrameBuffer.remaining();
-                    int rd = iob.read(inFrameBuffer, headRest);
+                if (currentInFrame == null) {
+                    if (inFrameBuffer.remaining() + avail < 8) {
+                        return;
+                    }
+                    if (inFrameBuffer.remaining() < 8) {
+                        int headRest = 8 - inFrameBuffer.remaining();
+                        int rd = iob.read(inFrameBuffer, headRest);
+                    }
+                    currentInFrame = new SpdyConnection.Frame(); // TODO: reuse
+                    currentInFrame.parse(this, inFrameBuffer);
                 }
-                currentInFrame = new SpdyConnection.Frame(); // TODO: reuse
-                currentInFrame.parse(this, inFrameBuffer);
-            }
-            if (iob.available() < currentInFrame.length) {
-                return;
-            }
-            // We have a full frame. Process it.
-            onFrame(iob);
+                if (iob.available() < currentInFrame.length) {
+                    return;
+                }
+                // We have a full frame. Process it.
+                onFrame(iob);
 
-            // TODO: extra checks, make sure the frame is correct and
-            // it consumed all data.
-            currentInFrame = null;
+                // TODO: extra checks, make sure the frame is correct and
+                // it consumed all data.
+                currentInFrame = null;
+            }
         }
     }
 
@@ -179,7 +185,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
                     ch.setHttpService(this.httpConnector.defaultService);
                 }
 
-                synchronized (this) {
+                synchronized (channels) {
                         channels.put(ch.channelId, ch);                    
                 }
 
@@ -206,7 +212,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
             } else if (currentInFrame.type == SpdyConnection.Frame.TYPE_SYN_REPLY) {
                 int chId = SpdyConnection.readInt(iob);
                 HttpChannel ch;
-                synchronized (this) {
+                synchronized (channels) {
                     ch = channels.get(chId);
                     if (ch == null) {
                         abort("Channel not found");
@@ -239,7 +245,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
             inDataFrames.incrementAndGet();
             // data frame - part of an existing stream
             HttpChannel ch;
-            synchronized (this) {
+            synchronized (channels) {
                 ch = channels.get(currentInFrame.streamId);
             }
             if (ch == null) {
@@ -280,8 +286,10 @@ public class SpdyConnection extends HttpConnector.HttpConnection
      */
     private void abort(String msg) throws IOException {
         streamErrors.incrementAndGet();
-        for (HttpChannel ch : channels.values()) {
-            ch.abort(msg);
+        synchronized(channels) {
+            for (HttpChannel ch : channels.values()) {
+                ch.abort(msg);
+            }
         }
         close();
     }
@@ -299,13 +307,13 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         if (headerCompression) {
             // 0x800 headers seems a bit too much - assume compressed.
             // I wish this was a flag...
-            headerCompressBuffer.recycle();
+            headerDeCompressBuffer.recycle();
             // stream id ( 4 ) + unused ( 2 ) 
             // nvCount is compressed in impl - spec is different
-            headCompressIn.decompress(iob, headerCompressBuffer, 
+            headCompressIn.decompress(iob, headerDeCompressBuffer, 
                     currentInFrame.length - 6);
-            headerCompressBuffer.copyAll(headRecvBuf);
-            headerCompressBuffer.recycle();
+            headerDeCompressBuffer.copyAll(headRecvBuf);
+            headerDeCompressBuffer.recycle();
             nvCount = readShort(headRecvBuf);
         } else {
             nvCount = readShort(iob);
@@ -424,7 +432,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         
         http.setConnection(this);
 
-        synchronized (this) {
+        synchronized (channels) {
             channels.put(http.channelId, http);            
         }
         
@@ -446,7 +454,9 @@ public class SpdyConnection extends HttpConnector.HttpConnection
 
     
     public synchronized Collection<HttpChannel> getActives() {
-        return channels.values();
+        synchronized(channels) {
+            return channels.values();
+        }
     }
     
     @Override
@@ -711,7 +721,7 @@ public class SpdyConnection extends HttpConnector.HttpConnection
     
     @Override
     protected void endSendReceive(HttpChannel http) throws IOException {
-        synchronized (this) {
+        synchronized (channels) {
             HttpChannel doneHttp = channels.remove(http.channelId);
             if (doneHttp != http) {
                 log.severe("Error removing " + doneHttp + " " + http);
@@ -775,8 +785,15 @@ public class SpdyConnection extends HttpConnector.HttpConnection
         }
         secure = httpCh.getRequest().isSecure();
         if (secure) {
-            SslChannel ch1 = new SslChannel();
-            ch1.setSslContext(httpConnector.sslConnector.getSSLContext());
+            if (httpConnector.debugHttp) {
+                IOChannel ch1 = new DumpChannel("NET-IN");
+                net.addFilterAfter(ch1);
+                net = ch1;
+            }
+            String[] hostPort = httpCh.getTarget().split(":");
+            
+            SslChannel ch1 = httpConnector.sslConnector.channel(
+                    hostPort[0], Integer.parseInt(hostPort[1]));
             ch1.setSink(net);
             net.addFilterAfter(ch1);
             net = ch1;
index 1319b27..47ea502 100644 (file)
@@ -46,7 +46,9 @@ public class DumpChannel extends IOChannel {
             }
             any = true;
             out("IN", first, false);
-            in.queue(first);
+            if (!in.isAppendClosed()) {
+                in.queue(first);
+            }
         }
     }
 
index 2553d62..f3a2cab 100644 (file)
@@ -342,8 +342,12 @@ public class IOBuffer {
     
     public int write(ByteBuffer bb) throws IOException {
         int len = bb.remaining();
+        int pos = bb.position();
+        if (len == 0) {
+            return 0;
+        }
         append(bb);
-        bb.position(bb.position() + len);
+        bb.position(pos + len);
         return len;
     }
     
index 9893cf6..99dbc88 100644 (file)
@@ -21,7 +21,7 @@ import java.nio.channels.ByteChannel;
  * @author Costin Manolache
  */
 public abstract class IOChannel implements ByteChannel, IOConnector.DataReceivedCallback, 
-        IOConnector.DataFlushedCallback { //, IOConnector.ClosedCallback {
+        IOConnector.DataFlushedCallback {  
     
     protected IOChannel net;
     protected IOChannel app;
@@ -39,6 +39,8 @@ public abstract class IOChannel implements ByteChannel, IOConnector.DataReceived
     // TODO: update, etc
     public long ts;
     
+    protected Throwable lastException;
+    
     public void setConnectedCallback(IOConnector.ConnectedCallback connectedCallback) {
         this.connectedCallback = connectedCallback;
     }
@@ -131,7 +133,18 @@ public abstract class IOChannel implements ByteChannel, IOConnector.DataReceived
             }
         } 
     }
-  
+
+    /** 
+     * Return last IO exception. 
+     * 
+     * The channel is async, exceptions can happen at any time. 
+     * The normal callback will be called ( connected, received ), it 
+     * should check if the channel is closed and the exception.
+     */
+    public Throwable lastException() {
+        return lastException;
+    }
+    
     public void close() throws IOException {
         shutdownOutput();
         // Should it read the buffers ? 
index 62eb0da..8946859 100644 (file)
@@ -21,6 +21,12 @@ public abstract class IOConnector {
         public void handleReceived(IOChannel ch) throws IOException;
     }
 
+    /** 
+     * Callback for accept and connect.
+     *
+     * Will also be called if an error happens while connecting, in 
+     * which case the connection will be closed.
+     */
     public static interface ConnectedCallback {
         public void handleConnected(IOChannel ch) throws IOException;
     }
@@ -35,6 +41,10 @@ public abstract class IOConnector {
         return timer;
     }
     
+    public IOConnector getNet() {
+        return null;
+    }
+    
     public abstract void acceptor(IOConnector.ConnectedCallback sc, 
                          CharSequence port, Object extra)
         throws IOException; 
index ad8f952..0c84f68 100644 (file)
@@ -10,7 +10,6 @@ import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;
-import java.util.BitSet;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -155,6 +154,40 @@ public class IOWriter extends Writer {
         }
     }
 
+    // TODO: use it for utf-8
+    public static int char2utf8(byte[] ba, int off, char c, char c1) {
+        int i = 0;
+        if (c < 0x80) {
+            ba[off++] = (byte) (c & 0xFF);
+            return 1;
+        } else if (c < 0x800)
+        {
+            ba[off++] = (byte) (0xC0 | c >> 6);
+            ba[off++] = (byte) (0x80 | c & 0x3F);
+            return 2;
+        }
+        else if (c < 0x10000)
+        {
+            ba[off++] = (byte) ((0xE0 | c >> 12));
+            ba[off++] = (byte) ((0x80 | c >> 6 & 0x3F));
+            ba[off++] = (byte) ((0x80 | c & 0x3F));
+            return 3;
+        }
+        else if (c < 0x200000)
+        {
+            ba[off++] = (byte) ((0xF0 | c >> 18));
+            ba[off++] = (byte) ((0x80 | c >> 12 & 0x3F));
+            ba[off++] = (byte) ((0x80 | c >> 6 & 0x3F));
+            ba[off++] = (byte) ((0x80 | c & 0x3F));
+            return 4;
+        }
+
+        
+        return i;
+    }
+    
+
+    
     /**
      * Just send the chars to the byte[], without flushing down.
      * 
index c8a136b..559a9e5 100644 (file)
@@ -5,7 +5,6 @@ import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.ByteChannel;
 import java.nio.channels.Channel;
-import java.nio.channels.SelectionKey;
 
 
 /** 
index d782ae3..f10d37a 100644 (file)
@@ -410,23 +410,17 @@ public class NioThread implements Runnable {
           }
 
           readInterest(ch, true);
-
-          if (ch.callback != null) {
-              ch.callback.handleConnected(ch);
-          }
       } catch (Throwable t) {
-          log.warning("Error in connect, closing " + t);
           close(ch, t);
-          try {
-              if (ch.callback != null) {
-                  ch.callback.handleConnected(ch);
-              }
-          } catch(Throwable t1) {
-              log.warning("Double error in connect, callback broken too");
-              t1.printStackTrace();
+      }
+      try {
+          if (ch.callback != null) {
+              ch.callback.handleConnected(ch);
           }
-          
+      } catch(Throwable t1) {
+          log.log(Level.WARNING, "Error in connect callback", t1);
       }
+
   }
 
   private void handleAccept(NioChannel ch, SelectionKey sk)
@@ -729,17 +723,17 @@ public class NioThread implements Runnable {
    */
   public int close(NioChannel selectorData, Throwable exception) throws IOException {
       synchronized (closeInterest) {
+          if (exception != null) {
+              selectorData.lastException = exception;
+          }
+          selectorData.readInterest = false;
           if (isSelectorThread()) {
               closeIOThread(selectorData, true);
               return 0;
           }
-          if (exception != null) {
-              selectorData.lastException = exception;
-          }
           if (!selectorData.inClosed) {
               closeInterest.add(selectorData);
           }
-          selectorData.readInterest = false;
       }
       selector.wakeup();
       return 0;
index 6c75d48..1fa662b 100644 (file)
@@ -239,6 +239,7 @@ public class SocketIOChannel extends IOChannel implements NioChannelCallback {
     
     @Override
     public void handleClosed(NioChannel ch) throws IOException {
+        lastException = ch.lastException;
         closed(); // our callback.
     }
     
index 163228a..e2ae08a 100644 (file)
@@ -5,12 +5,14 @@ package org.apache.tomcat.lite.io;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
+import java.util.concurrent.TimeoutException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 import javax.net.ssl.SSLEngineResult.Status;
@@ -30,9 +32,13 @@ public class SslChannel extends IOChannel implements Runnable {
     
     IOBuffer in = new IOBuffer(this);
     IOBuffer out = new IOBuffer(this);
+
+    long handshakeTimeout = 10000;
+    // Used for session reuse
+    String host;
+    int port;
     
     public SslChannel() {
-        
     }
     
     ByteBuffer myAppOutData;
@@ -52,13 +58,30 @@ public class SslChannel extends IOChannel implements Runnable {
 
     private boolean closeHandshake = false;
     
-    private void initSsl() throws GeneralSecurityException {
+    /**
+     * Setting the host/port enables clients to reuse SSL session - 
+     * less traffic and encryption overhead at startup, assuming the 
+     * server caches the session ( i.e. single server or distributed cache ).
+     * 
+     * SSL ticket extension is another possibility.
+     */
+    public SslChannel setTarget(String host, int port) {
+        this.host = host;
+        this.port = port;
+        return this;
+    }
+    
+    private synchronized void initSsl() throws GeneralSecurityException {
         if (sslEngine != null) {
             return;
         }
         
         if (client) {
-            sslEngine = sslCtx.createSSLEngine();
+            if (port > 0) {
+                sslEngine = sslCtx.createSSLEngine(host, port);
+            } else {
+                sslEngine = sslCtx.createSSLEngine();
+            }
             sslEngine.setUseClientMode(client);
         } else {
             sslEngine = sslCtx.createSSLEngine();
@@ -89,7 +112,7 @@ public class SslChannel extends IOChannel implements Runnable {
     
     
     @Override
-    public void setSink(IOChannel net) throws IOException {
+    public synchronized void setSink(IOChannel net) throws IOException {
         try {
             initSsl();
             super.setSink(net);
@@ -97,7 +120,7 @@ public class SslChannel extends IOChannel implements Runnable {
             log.log(Level.SEVERE, "Error initializing ", e);
         }
     }
-
+    
     @Override
     public IOBuffer getIn() {
         return in;
@@ -118,7 +141,7 @@ public class SslChannel extends IOChannel implements Runnable {
      */
     public int processInput(IOBuffer netIn, IOBuffer appIn) throws IOException {
         if (log.isLoggable(Level.FINEST)) {
-            log.finest("JSSE: processInput " + handshakeInProgress + " " + netIn.getBufferCount());
+            log.info("JSSE: processInput " + handshakeInProgress + " " + netIn.getBufferCount());
         }
         if (!handshakeDone && !handshakeInProgress) {
             handshakeInProgress = true;
@@ -134,6 +157,7 @@ public class SslChannel extends IOChannel implements Runnable {
     private synchronized int processRealInput(IOBuffer netIn, IOBuffer appIn) throws IOException {
         int rd = 0;
         boolean needsMore = true;
+        boolean notEnough = false;
 
         while (needsMore) {
             if (netIn.isClosedAndEmpty()) {
@@ -142,9 +166,14 @@ public class SslChannel extends IOChannel implements Runnable {
                 return -1;
             }
             myNetInData.compact();
-            int rdNow = netIn.read(myNetInData);
-            myNetInData.flip();
-            if (rdNow == 0) {
+            int rdNow;
+            try {
+                rdNow = netIn.read(myNetInData);
+            } finally {
+                myNetInData.flip();
+            }
+            if (rdNow == 0 && (myNetInData.remaining() == 0 || 
+                    notEnough)) {
                 return rd;
             }
             if (rdNow == -1) {
@@ -153,10 +182,18 @@ public class SslChannel extends IOChannel implements Runnable {
                 return rd;
             }
 
+            notEnough = true; // next read of 0
             while (myNetInData.remaining() > 0) {
                 myAppInData.compact();
-                unwrapR = sslEngine.unwrap(myNetInData, myAppInData);
-                myAppInData.flip();
+                try {
+                    unwrapR = sslEngine.unwrap(myNetInData, myAppInData);
+                } catch (SSLException ex) {
+                    log.warning("Read error: " + ex);
+                    close();
+                    return -1;
+                } finally {
+                    myAppInData.flip();
+                }
                 if (myAppInData.remaining() > 0) {
                     in.write(myAppInData); // all will be written
                 }
@@ -216,50 +253,78 @@ public class SslChannel extends IOChannel implements Runnable {
     }
     
     public void close() throws IOException {
+        if (net.getOut().isAppendClosed()) {
+            return;
+        }
         sslEngine.closeOutbound(); // mark as closed
-        myNetOutData.compact();
-        SSLEngineResult wrap = sslEngine.wrap(EMPTY, 
-                myNetOutData);
-        myNetOutData.flip();
-        if (wrap.getStatus() != Status.CLOSED) {
-            System.err.println("Unexpected status " + wrap);
+        synchronized(myNetOutData) {
+            myNetOutData.compact();
+
+            SSLEngineResult wrap;
+            try {
+                wrap = sslEngine.wrap(EMPTY, myNetOutData);
+                if (wrap.getStatus() != Status.CLOSED) {
+                    log.warning("Unexpected close status " + wrap);
+                }
+            } catch (Throwable t ) {
+                log.info("Error wrapping " + myNetOutData);
+            } finally {
+                myNetOutData.flip();
+            }
+            if (myNetOutData.remaining() > 0) {
+                net.getOut().write(myNetOutData);
+            }
         }
-        net.getOut().write(myNetOutData);
         // TODO: timer to close socket if we don't get
         // clean close handshake
+        super.close();
     }
     
-    private synchronized void startRealSending() throws IOException {
-        while (true) {
-        
-            myAppOutData.compact();
-            int rd = out.read(myAppOutData);
-            myAppOutData.flip();
-            if (rd == 0) {
-                break;
-            }
-            if (rd < 0) {
-                close();
-                break;
-            }
+    private Object sendLock = new Object();
     
-            myNetOutData.compact();
-            SSLEngineResult wrap = sslEngine.wrap(myAppOutData, 
-                    myNetOutData);
-            myNetOutData.flip();
-            net.getOut().write(myNetOutData);
-                        
-            if (wrap != null) {
-                switch (wrap.getStatus()) {
-                case BUFFER_UNDERFLOW: {
+    private void startRealSending() throws IOException {
+        // Only one thread at a time
+        synchronized (sendLock) {
+            while (true) {
+                
+                myAppOutData.compact();
+                int rd;
+                try {
+                    rd = out.read(myAppOutData);
+                } finally {
+                    myAppOutData.flip();
+                }
+                if (rd == 0) {
                     break;
                 }
-                case OK: {
+                if (rd < 0) {
+                    close();
                     break;
                 }
-                case BUFFER_OVERFLOW: {
-                    throw new IOException("Overflow");
+
+                SSLEngineResult wrap;
+                synchronized(myNetOutData) {
+                    myNetOutData.compact();
+                    try {
+                        wrap = sslEngine.wrap(myAppOutData, 
+                                myNetOutData);
+                    } finally {
+                        myNetOutData.flip();
+                    }
+                    net.getOut().write(myNetOutData);
                 }
+                if (wrap != null) {
+                    switch (wrap.getStatus()) {
+                    case BUFFER_UNDERFLOW: {
+                        break;
+                    }
+                    case OK: {
+                        break;
+                    }
+                    case BUFFER_OVERFLOW: {
+                        throw new IOException("Overflow");
+                    }
+                    }
                 }
             }
         }
@@ -275,16 +340,16 @@ public class SslChannel extends IOChannel implements Runnable {
     // We'll need to unregister and register again from the selector.
     private void handleHandshking() { 
         if (log.isLoggable(Level.FINEST)) {
-            log.finest("Starting handshake");
+            log.info("Starting handshake");
         }
         handshakeInProgress = true;
 
-        new Thread(this).start();
+        ((SslConnector) connector).handshakeExecutor.execute(this);
     }
     
     private void endHandshake() throws IOException {
         if (log.isLoggable(Level.FINEST)) {
-            log.finest("Handshake done");
+            log.info("Handshake done " + net.getIn().available());
         }
         handshakeDone = true;
         handshakeInProgress = false;
@@ -292,6 +357,10 @@ public class SslChannel extends IOChannel implements Runnable {
             flushing = false;
             startSending();
         }
+        if (myNetInData.remaining() > 0 || net.getIn().available() > 0) {
+            // Last SSL packet also includes data.
+            handleReceived(net);
+        }
     }
 
     /**
@@ -311,26 +380,41 @@ public class SslChannel extends IOChannel implements Runnable {
             
             long t0 = System.currentTimeMillis();
             
-            while (hstatus != HandshakeStatus.FINISHED) {
+            while (hstatus != HandshakeStatus.NOT_HANDSHAKING 
+                    && hstatus != HandshakeStatus.FINISHED 
+                    && !net.getIn().isAppendClosed()) {
+                if (System.currentTimeMillis() - t0 > handshakeTimeout) {
+                    throw new TimeoutException();
+                }
                 if (wrap != null && wrap.getStatus() == Status.CLOSED) {
                     break;
                 }
                 if (log.isLoggable(Level.FINEST)) {
-                    log.finest("-->doHandshake() loop: status = " + hstatus + " " +
+                    log.info("-->doHandshake() loop: status = " + hstatus + " " +
                             sslEngine.getHandshakeStatus());
                 }
                 
                 if (hstatus == HandshakeStatus.NEED_WRAP) {
                     // || initial - for client
                     initial = false;
-                    myNetOutData.compact();
-                    
-                    wrap = sslEngine.wrap(myAppOutData, myNetOutData);
-                    myNetOutData.flip();
-                    
-                    hstatus = wrap.getHandshakeStatus();
+                    synchronized(myNetOutData) {
+                        myNetOutData.compact();
 
-                    net.getOut().write(myNetOutData);
+                        try {
+                            wrap = sslEngine.wrap(myAppOutData, myNetOutData);
+                        } catch (Throwable t) {
+                            t.printStackTrace();
+                            close();
+                            return;
+                        } finally {
+                            myNetOutData.flip();
+                        }
+
+                        hstatus = wrap.getHandshakeStatus();
+                        if (myNetOutData.remaining() > 0) {
+                            net.getOut().write(myNetOutData);
+                        }
+                    }
                     net.startSending();
 
                     
@@ -356,11 +440,17 @@ public class SslChannel extends IOChannel implements Runnable {
                                 || wrap.getStatus() == Status.BUFFER_UNDERFLOW
                                 || (hstatus == HandshakeStatus.NEED_UNWRAP && myNetInData.remaining() == 0)) {
                             myNetInData.compact();
-                            int rd = net.getIn().read(myNetInData);
-                            myNetInData.flip();
+                            // non-blocking
+                            int rd;
+                            try {
+                                rd = net.getIn().read(myNetInData);
+                            } finally {
+                                myNetInData.flip();
+                            }
                             if (rd == 0) {
-                                net.getIn().waitData(10000);
-                                continue;
+                                net.getIn().waitData(handshakeTimeout - 
+                                        (System.currentTimeMillis() - t0));
+                                rd = net.getIn().read(myNetInData);
                             }
                             if (rd < 0) {
                                 // in closed
@@ -368,7 +458,7 @@ public class SslChannel extends IOChannel implements Runnable {
                             }
                         }
                         if (log.isLoggable(Level.FINEST)) {
-                            log.finest("Unwrap chunk done " + hstatus + " " + wrap 
+                            log.info("Unwrap chunk done " + hstatus + " " + wrap 
                                 + " " + sslEngine.getHandshakeStatus());
                         }
 
@@ -384,7 +474,7 @@ public class SslChannel extends IOChannel implements Runnable {
                     long t1task = System.currentTimeMillis();
                     hstatus = sslEngine.getHandshakeStatus();
                     if (log.isLoggable(Level.FINEST)) {
-                        log.finest("Tasks done in " + (t1task - t0task) + " new status " +
+                        log.info("Tasks done in " + (t1task - t0task) + " new status " +
                                 hstatus);
                     }
                     
@@ -401,6 +491,7 @@ public class SslChannel extends IOChannel implements Runnable {
             try {
                 close();
                 net.close();
+                sendHandleReceivedCallback();
             } catch (IOException ex) {
                 log.log(Level.SEVERE, "Error closing", ex);                
             }
@@ -419,4 +510,9 @@ public class SslChannel extends IOChannel implements Runnable {
         this.sslCtx = sslCtx;
         return this;
     }
+    
+    public SslChannel setSslConnector(SslConnector con) {
+        this.connector = con;
+        return this;
+    }
 }
index 04acbee..cdb4ace 100644 (file)
@@ -19,7 +19,6 @@ import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.PrivateKey;
-import java.security.PublicKey;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
@@ -28,8 +27,9 @@ import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.RSAKeyGenParameterSpec;
-import java.security.spec.X509EncodedKeySpec;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
@@ -42,6 +42,12 @@ import javax.net.ssl.X509TrustManager;
 
 public class SslConnector extends IOConnector {
 
+    /**
+     * TODO: option to require validation.
+     * TODO: remember cert signature. This is needed to support self-signed 
+     * certs, like those used by the test. 
+     * 
+     */
     public static class BasicTrustManager implements X509TrustManager {
     
         private X509Certificate[] chain;
@@ -64,20 +70,21 @@ public class SslConnector extends IOConnector {
     public static TrustManager[] trustAllCerts = new TrustManager[] { 
         new BasicTrustManager() }; 
 
-    static final boolean debug = false;
 
-    static {
-        if (debug) {
-            System.setProperty("javax.net.debug", "ssl");
-        }
-    }
+    static final boolean debug = false;
+    
     IOConnector net;
     private KeyManager[] keyManager; 
     SSLContext sslCtx;
     boolean server;
     private TrustManager[] trustManagers;
     
-    Executor handshakeExecutor;
+    public AtomicInteger handshakeCount = new AtomicInteger();
+    public AtomicInteger handshakeOk = new AtomicInteger();
+    public AtomicInteger handshakeErr = new AtomicInteger();
+    public AtomicInteger handshakeTime = new AtomicInteger();
+    
+    Executor handshakeExecutor = Executors.newCachedThreadPool();
     static int id = 0;
     
     public SslConnector() {
@@ -92,7 +99,7 @@ public class SslConnector extends IOConnector {
             try {
                 sslCtx = SSLContext.getInstance("TLS");
                 if (trustManagers == null) {
-                    trustManagers =
+                    trustManagers = 
                         new TrustManager[] {new BasicTrustManager()}; 
 
                 }
@@ -115,6 +122,18 @@ public class SslConnector extends IOConnector {
         return net;
     }
     
+    public SslChannel channel(String host, int port) {
+        return new SslChannel()
+            .setTarget(host, port)
+            .setSslContext(getSSLContext())
+            .setSslConnector(this);
+    }
+
+    public SslChannel serverChannel() {
+        return new SslChannel()
+            .setSslContext(getSSLContext())
+            .setSslConnector(this).withServer();
+    }
     
     @Override
     public void acceptor(final ConnectedCallback sc, CharSequence port, Object extra) 
@@ -129,9 +148,7 @@ public class SslConnector extends IOConnector {
                     first = dch;
                 }
                 
-                IOChannel sslch = new SslChannel()
-                    .setSslContext(sslCtx)
-                    .withServer();
+                IOChannel sslch = serverChannel();
                 sslch.setSink(first);
                 first.addFilterAfter(sslch);
 
@@ -148,7 +165,7 @@ public class SslConnector extends IOConnector {
     }
     
     @Override
-    public void connect(String host, int port, final ConnectedCallback sc)
+    public void connect(final String host, final int port, final ConnectedCallback sc)
             throws IOException {
         getNet().connect(host, port, new ConnectedCallback() {
 
@@ -161,8 +178,7 @@ public class SslConnector extends IOConnector {
                     first = dch;
                 }
                 
-                IOChannel sslch = new SslChannel()
-                    .setSslContext(sslCtx);
+                IOChannel sslch = channel(host, port);
                 sslch.setSink(first);
                 first.addFilterAfter(sslch);
 
@@ -318,7 +334,7 @@ public class SslConnector extends IOConnector {
 
     public static void fixUrlConnection() {
         try {
-            SSLContext sc = SSLContext.getInstance("SSL");
+            SSLContext sc = SSLContext.getInstance("TLS");
             sc.init(null, SslConnector.trustAllCerts, null);
             javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
                     sc.getSocketFactory());
index 684b85c..d641cb6 100644 (file)
@@ -33,147 +33,33 @@ import org.apache.tomcat.lite.http.HttpRequest;
 
 public class ServletApi30  extends ServletApi {
     public ServletContextImpl newContext() {
-        return new ServletContextImpl() {
-            protected void initEngineDefaults() throws ServletException {
-                super.initEngineDefaults();
-                setAttribute(InstanceManager.class.getName(),
-                        new LiteInstanceManager(getObjectManager()));
-            }
-            @Override
-            public Dynamic addFilter(String filterName, String className) {
-                FilterConfigImpl fc = new FilterConfigImpl(this);
-                fc.setData(filterName, null, new HashMap());
-                fc.setData(filterName, className, new HashMap());
-                filters.put(filterName, fc);
-                return new DynamicFilterRegistration(fc);
-            }
-
-            @Override
-            public Dynamic addFilter(String filterName, Filter filter) {
-                FilterConfigImpl fc = new FilterConfigImpl(this);
-                fc.setData(filterName, null, new HashMap());
-                fc.setFilter(filter);
-                filters.put(filterName, fc);
-                return new DynamicFilterRegistration(fc);
-            }
-
-            @Override
-            public Dynamic addFilter(String filterName,
-                    Class<? extends Filter> filterClass) {
-                FilterConfigImpl fc = new FilterConfigImpl(this);
-                fc.setData(filterName, null, new HashMap());
-                fc.setFilterClass(filterClass);
-                filters.put(filterName, fc);
-                return new DynamicFilterRegistration(fc);
-            }
-
-            @Override
-            public javax.servlet.ServletRegistration.Dynamic addServlet(
-                    String servletName, String className) {
-                return null;
-            }
-
-            @Override
-            public javax.servlet.ServletRegistration.Dynamic addServlet(
-                    String servletName, Servlet servlet) {
-                return null;
-            }
-
-            @Override
-            public javax.servlet.ServletRegistration.Dynamic addServlet(
-                    String servletName, Class<? extends Servlet> servletClass) {
-                return null;
-            }
-
-
-            @Override
-            public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
-                return null;
-            }
-
-
-            @Override
-            public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
-                return null;
-            }
-
-            @Override
-            public FilterRegistration getFilterRegistration(String filterName) {
-                return null;
-            }
-
-            @Override
-            public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
-                return null;
-            }
-
-            @Override
-            public JspConfigDescriptor getJspConfigDescriptor() {
-                return null;
-            }
-
-            @Override
-            public ServletRegistration getServletRegistration(String servletName) {
-                return null;
-            }
-
-            @Override
-            public Map<String, ? extends ServletRegistration> getServletRegistrations() {
-                return null;
-            }
-
-            @Override
-            public SessionCookieConfig getSessionCookieConfig() {
-                return null;
-            }
-
-            @Override
-            public void setSessionTrackingModes(
-                    EnumSet<SessionTrackingMode> sessionTrackingModes)
-                    throws IllegalStateException, IllegalArgumentException {
-            }
-            
-            public int getMajorVersion() {
-                return 3;
-            }
-            
-            public int getMinorVersion() {
-                return 0;
-            }
-            
-        };
+        return new Servlet30ContextImpl();
     }
 
     public ServletRequestImpl newRequest(HttpRequest req) {
         return new ServletRequestImpl(req) {
 
-            @Override
             public Part getPart(String name) {
                 return null;
             }
 
-            @Override
             public Collection<Part> getParts() throws IOException,
                     ServletException {
                 return null;
             }
 
-            @Override
             public AsyncContext getAsyncContext() {
                 return null;
             }
 
-            @Override
             public DispatcherType getDispatcherType() {
                 return null;
             }
 
-            @Override
             public AsyncContext startAsync() {
                 return null;
             }
 
-            @Override
             public AsyncContext startAsync(ServletRequest servletRequest,
                     ServletResponse servletResponse) {
                 return null;
@@ -182,6 +68,99 @@ public class ServletApi30  extends ServletApi {
         };
     }
     
+    public final class Servlet30ContextImpl extends ServletContextImpl {
+        protected void initEngineDefaults() throws ServletException {
+            super.initEngineDefaults();
+            setAttribute(InstanceManager.class.getName(),
+                    new LiteInstanceManager(getObjectManager()));
+        }
+
+        public Dynamic addFilter(String filterName, String className) {
+            FilterConfigImpl fc = new FilterConfigImpl(this);
+            fc.setData(filterName, null, new HashMap());
+            fc.setData(filterName, className, new HashMap());
+            filters.put(filterName, fc);
+            return new DynamicFilterRegistration(fc);
+        }
+
+        public Dynamic addFilter(String filterName, Filter filter) {
+            FilterConfigImpl fc = new FilterConfigImpl(this);
+            fc.setData(filterName, null, new HashMap());
+            fc.setFilter(filter);
+            filters.put(filterName, fc);
+            return new DynamicFilterRegistration(fc);
+        }
+
+        public Dynamic addFilter(String filterName,
+                Class<? extends Filter> filterClass) {
+            FilterConfigImpl fc = new FilterConfigImpl(this);
+            fc.setData(filterName, null, new HashMap());
+            fc.setFilterClass(filterClass);
+            filters.put(filterName, fc);
+            return new DynamicFilterRegistration(fc);
+        }
+
+        public javax.servlet.ServletRegistration.Dynamic addServlet(
+                String servletName, String className) {
+            return null;
+        }
+
+        public javax.servlet.ServletRegistration.Dynamic addServlet(
+                String servletName, Servlet servlet) {
+            return null;
+        }
+
+        public javax.servlet.ServletRegistration.Dynamic addServlet(
+                String servletName, Class<? extends Servlet> servletClass) {
+            return null;
+        }
+
+        public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+            return null;
+        }
+
+        public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+            return null;
+        }
+
+        public FilterRegistration getFilterRegistration(String filterName) {
+            return null;
+        }
+
+        public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+            return null;
+        }
+
+        public JspConfigDescriptor getJspConfigDescriptor() {
+            return null;
+        }
+
+        public ServletRegistration getServletRegistration(String servletName) {
+            return null;
+        }
+
+        public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+            return null;
+        }
+
+        public SessionCookieConfig getSessionCookieConfig() {
+            return null;
+        }
+
+        public int getMajorVersion() {
+            return 3;
+        }
+
+        public int getMinorVersion() {
+            return 0;
+        }
+
+        public void setSessionTrackingModes(
+                Set<SessionTrackingMode> sessionTrackingModes)
+                throws IllegalStateException, IllegalArgumentException {
+        }
+    }
+
     private final class LiteInstanceManager implements InstanceManager {
         private ObjectManager om;
 
index 78e96cc..002b192 100644 (file)
@@ -53,6 +53,8 @@ import javax.servlet.ServletException;
 import org.apache.tomcat.integration.ObjectManager;
 import org.apache.tomcat.lite.http.BaseMapper;
 import org.apache.tomcat.lite.io.FileConnectorJavaIo;
+import org.apache.tomcat.lite.util.MimeMap;
+import org.apache.tomcat.lite.util.UrlUtils;
 import org.apache.tomcat.servlets.config.ConfigLoader;
 import org.apache.tomcat.servlets.config.ServletContextConfig;
 import org.apache.tomcat.servlets.config.ServletContextConfig.FilterData;
@@ -60,9 +62,7 @@ import org.apache.tomcat.servlets.config.ServletContextConfig.FilterMappingData;
 import org.apache.tomcat.servlets.config.ServletContextConfig.ServletData;
 import org.apache.tomcat.servlets.session.UserSessionManager;
 import org.apache.tomcat.servlets.util.Enumerator;
-import org.apache.tomcat.servlets.util.MimeMap;
 import org.apache.tomcat.servlets.util.RequestUtil;
-import org.apache.tomcat.servlets.util.UrlUtils;
 
 
 /**
index a2e430c..8b9baa7 100644 (file)
@@ -54,9 +54,9 @@ import org.apache.tomcat.lite.http.MultiMap.Entry;
 import org.apache.tomcat.lite.io.BBuffer;
 import org.apache.tomcat.lite.io.CBuffer;
 import org.apache.tomcat.lite.io.FastHttpDateFormat;
+import org.apache.tomcat.lite.util.LocaleParser;
 import org.apache.tomcat.servlets.session.UserSessionManager;
 import org.apache.tomcat.servlets.util.Enumerator;
-import org.apache.tomcat.servlets.util.LocaleParser;
 import org.apache.tomcat.servlets.util.RequestUtil;
 
 
@@ -206,8 +206,6 @@ public abstract class ServletRequestImpl implements HttpServletRequest {
     public static final String WORK_DIR_ATTR =
         "javax.servlet.context.tempdir";
 
-    protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
-
 
     /**
      * The default Locale if none are specified.
@@ -228,13 +226,6 @@ public abstract class ServletRequestImpl implements HttpServletRequest {
     protected Cookie[] cookies = null;
 
 
-    /**
-     * The set of SimpleDateFormat formats to use in getDateHeader().
-     *
-     * Notice that because SimpleDateFormat is not thread-safe, we can't
-     * declare formats[] as a static variable.
-     */
-    protected SimpleDateFormat formats[] = null;
     
 
     /**
@@ -683,28 +674,7 @@ public abstract class ServletRequestImpl implements HttpServletRequest {
      *  cannot be converted to a date
      */
     public long getDateHeader(String name) {
-
-        String value = getHeader(name);
-        if (value == null)
-            return (-1L);
-        if (formats == null) {
-            formats = new SimpleDateFormat[] {
-                new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
-                new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
-                new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
-            };
-            formats[0].setTimeZone(GMT_ZONE);
-            formats[1].setTimeZone(GMT_ZONE);
-            formats[2].setTimeZone(GMT_ZONE);
-        }
-        
-        // Attempt to convert the date header in a variety of formats
-        long result = FastHttpDateFormat.parseDate(value, formats);
-        if (result != (-1L)) {
-            return result;
-        }
-        throw new IllegalArgumentException(value);
-
+        return httpRequest.getDateHeader(name);
     }
 
     /**
index 791759c..9fcfc73 100644 (file)
@@ -32,6 +32,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.tomcat.lite.util.URLEncoder;
 import org.apache.tomcat.servlets.util.RequestUtil;
 
 /**
@@ -430,7 +431,7 @@ public class WebappFilterMapper implements Filter {
         }
 
         public void setURLPattern(String urlPattern) {
-            this.urlPattern = RequestUtil.URLDecode(urlPattern);
+            this.urlPattern = URLEncoder.URLDecode(urlPattern);
         }
         
         /**
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Base64.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Base64.java
new file mode 100644 (file)
index 0000000..848c99d
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ *  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.lite.util;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as
+ * defined by RFC 2045, N. Freed and N. Borenstein.
+ * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
+ * Part One: Format of Internet Message Bodies. Reference
+ * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
+ * This class is used by XML Schema binary format validation
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Revision: 467222 $ $Date: 2006-10-23 20:17:11 -0700 (Mon, 23 Oct 2006) $
+ */
+
+public final class Base64 {
+
+
+    private static Logger log=
+        Logger.getLogger( Base64.class.getName() );
+    
+    static private final int  BASELENGTH         = 255;
+    static private final int  LOOKUPLENGTH       = 63;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+
+
+    static private final byte PAD               = ( byte ) '=';
+    static private byte [] base64Alphabet       = new byte[BASELENGTH];
+    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH + 1];
+
+    static {
+
+        for (int i = 0; i<BASELENGTH; i++ ) {
+            base64Alphabet[i] = -1;
+        }
+        for ( int i = 'Z'; i >= 'A'; i-- ) {
+            base64Alphabet[i] = (byte) (i-'A');
+        }
+        for ( int i = 'z'; i>= 'a'; i--) {
+            base64Alphabet[i] = (byte) ( i-'a' + 26);
+        }
+
+        for ( int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i-'0' + 52);
+        }
+
+        base64Alphabet['+']  = 62;
+        base64Alphabet['/']  = 63;
+        String lookupString = 
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
+            "0123456789+/";
+        
+       for (int i = 0; i < lookupString.length(); i++ ) {
+            lookUpBase64Alphabet[i] = (byte) lookupString.charAt(i);
+       }
+    }
+
+
+    static boolean isBase64( byte octect ) {
+        //shall we ignore white space? JEFF??
+        return(octect == PAD || base64Alphabet[octect] != -1 );
+    }
+
+
+    static boolean isArrayByteBase64( byte[] arrayOctect ) {
+        int length = arrayOctect.length;
+        if ( length == 0 )
+            return false;
+        for ( int i=0; i < length; i++ ) {
+            if ( Base64.isBase64( arrayOctect[i] ) == false)
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static byte[] encode( byte[] binaryData ) {
+        int      lengthDataBits    = binaryData.length*EIGHTBIT;
+        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
+        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
+        byte     encodedData[]     = null;
+
+
+        if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
+            encodedData = new byte[ (numberTriplets + 1 )*4  ];
+        else // 16 or 8 bit
+            encodedData = new byte[ numberTriplets*4 ];
+
+        int k=0, l=0, b1=0,b2=0,b3=0;
+
+        int encodedIndex = 0;
+        int dataIndex   = 0;
+        int i           = 0;
+        for ( i = 0; i<numberTriplets; i++ ) {
+
+            dataIndex = i*3;
+            b1 = binaryData[dataIndex] & 0xFF;
+            b2 = binaryData[dataIndex + 1] & 0xFF;
+            b3 = binaryData[dataIndex + 2] & 0xFF;
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            encodedIndex = i*4;
+            encodedData[encodedIndex]   = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
+( k<<4 )];
+            encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
+( b3>>6)];
+            encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex    = i*3;
+        encodedIndex = i*4;
+        if (fewerThan24bits == EIGHTBIT ) {
+            b1 = binaryData[dataIndex];
+            k = (byte) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if ( fewerThan24bits == SIXTEENBIT ) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex +1 ];
+            l = ( byte ) ( b2 &0x0f );
+            k = ( byte ) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
+| ( k<<4 )];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+        return encodedData;
+    }
+
+    public byte[] decode(String enc) {
+        return decode(enc.getBytes());
+    }
+    
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode( byte[] base64Data ) {
+        int      numberQuadruple    = base64Data.length/FOURBYTE;
+        byte     decodedData[]      = null;
+        byte     b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
+
+        // Throw away anything not in base64Data
+        // Adjust size
+
+        int encodedIndex = 0;
+        int dataIndex    = 0;
+        decodedData      = new byte[ numberQuadruple*3 + 1 ];
+
+        for (int i = 0; i<numberQuadruple; i++ ) {
+            dataIndex = i*4;
+            marker0   = base64Data[dataIndex +2];
+            marker1   = base64Data[dataIndex +3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex +1]];
+
+            if ( marker0 != PAD && marker1 != PAD ) {     //No PAD e.g 3cQl
+                b3 = base64Alphabet[ marker0 ];
+                b4 = base64Alphabet[ marker1 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
+            } else if ( marker0 == PAD ) {               //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
+                decodedData[encodedIndex+2] = (byte) 0;
+            } else if ( marker1 == PAD ) {              //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[ marker0 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 );
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6);
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+
+    }
+
+    static final int base64[]= {
+       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+           52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+           64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+           15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+           64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+           41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+    };
+
+    public static String base64Decode( String orig ) {
+       char chars[]=orig.toCharArray();
+       StringBuffer sb=new StringBuffer();
+       int i=0;
+
+       int shift = 0;   // # of excess bits stored in accum
+       int acc = 0;
+       
+       for (i=0; i<chars.length; i++) {
+           int v = base64[ chars[i] & 0xFF ];
+           
+           if ( v >= 64 ) {
+               if( chars[i] != '=' )
+                   if (log.isLoggable(Level.INFO))
+                       log.info("Wrong char in base64: " + chars[i]);
+           } else {
+               acc= ( acc << 6 ) | v;
+               shift += 6;
+               if ( shift >= 8 ) {
+                   shift -= 8;
+                   sb.append( (char) ((acc >> shift) & 0xff));
+               }
+           }
+       }
+       return sb.toString();
+    }
+
+
+}
+
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/FastHttpDateFormat.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/FastHttpDateFormat.java
new file mode 100644 (file)
index 0000000..fe3c69a
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  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.lite.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utility class to generate HTTP dates.
+ * 
+ * @author Remy Maucherat
+ */
+public final class FastHttpDateFormat {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    protected static final int CACHE_SIZE = 
+        Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
+
+    
+    /**
+     * HTTP date format.
+     */
+    protected static final SimpleDateFormat format = 
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     */
+    protected static final SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+
+        format.setTimeZone(gmtZone);
+
+        formats[0].setTimeZone(gmtZone);
+        formats[1].setTimeZone(gmtZone);
+        formats[2].setTimeZone(gmtZone);
+
+    }
+
+
+    /**
+     * Instant on which the currentDate object was generated.
+     */
+    protected static long currentDateGenerated = 0L;
+
+
+    /**
+     * Current formatted date.
+     */
+    protected static String currentDate = null;
+
+
+    /**
+     * Formatter cache.
+     */
+    protected static final ConcurrentHashMap<Long, String> formatCache = 
+        new ConcurrentHashMap<Long, String>(CACHE_SIZE);
+
+
+    /**
+     * Parser cache.
+     */
+    protected static final ConcurrentHashMap<String, Long> parseCache = 
+        new ConcurrentHashMap<String, Long>(CACHE_SIZE);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Get the current date in HTTP format.
+     */
+    public static final String getCurrentDate() {
+
+        long now = System.currentTimeMillis();
+        if ((now - currentDateGenerated) > 1000) {
+            synchronized (format) {
+                if ((now - currentDateGenerated) > 1000) {
+                    currentDateGenerated = now;
+                    currentDate = format.format(new Date(now));
+                }
+            }
+        }
+        return currentDate;
+
+    }
+
+
+    /**
+     * Get the HTTP format of the specified date.
+     */
+    public static final String formatDate
+        (long value, DateFormat threadLocalformat) {
+
+        Long longValue = new Long(value);
+        String cachedDate = formatCache.get(longValue);
+        if (cachedDate != null)
+            return cachedDate;
+
+        String newDate = null;
+        Date dateValue = new Date(value);
+        if (threadLocalformat != null) {
+            newDate = threadLocalformat.format(dateValue);
+            updateFormatCache(longValue, newDate);
+        } else {
+            synchronized (formatCache) {
+                synchronized (format) {
+                    newDate = format.format(dateValue);
+                }
+                updateFormatCache(longValue, newDate);
+            }
+        }
+        return newDate;
+
+    }
+
+
+    /**
+     * Try to parse the given date as a HTTP date.
+     */
+    public static final long parseDate(String value, 
+                                       DateFormat[] threadLocalformats) {
+
+        Long cachedDate = parseCache.get(value);
+        if (cachedDate != null)
+            return cachedDate.longValue();
+
+        Long date = null;
+        if (threadLocalformats != null) {
+            date = internalParseDate(value, threadLocalformats);
+            updateParseCache(value, date);
+        } else {
+            synchronized (parseCache) {
+                date = internalParseDate(value, formats);
+                updateParseCache(value, date);
+            }
+        }
+        if (date == null) {
+            return (-1L);
+        } else {
+            return date.longValue();
+        }
+
+    }
+
+
+    /**
+     * Parse date with given formatters.
+     */
+    private static final Long internalParseDate
+        (String value, DateFormat[] formats) {
+        Date date = null;
+        for (int i = 0; (date == null) && (i < formats.length); i++) {
+            try {
+                date = formats[i].parse(value);
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        if (date == null) {
+            return null;
+        }
+        return new Long(date.getTime());
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static void updateFormatCache(Long key, String value) {
+        if (value == null) {
+            return;
+        }
+        if (formatCache.size() > CACHE_SIZE) {
+            formatCache.clear();
+        }
+        formatCache.put(key, value);
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static void updateParseCache(String key, Long value) {
+        if (value == null) {
+            return;
+        }
+        if (parseCache.size() > CACHE_SIZE) {
+            parseCache.clear();
+        }
+        parseCache.put(key, value);
+    }
+
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/LocaleParser.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/LocaleParser.java
new file mode 100644 (file)
index 0000000..fafdbff
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * 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.lite.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) {
+          StringBuilder sb = new StringBuilder();
+          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);
+
+    }
+
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/MimeMap.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/MimeMap.java
new file mode 100644 (file)
index 0000000..bedb0ff
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ *  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.lite.util;
+
+import java.net.FileNameMap;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+/**
+ * A mime type map that implements the java.net.FileNameMap interface.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ */
+public class MimeMap implements FileNameMap {
+
+    // Defaults - all of them are "well-known" types,
+    // you can add using normal web.xml.
+    
+    public static Hashtable<String,String> defaultMap =
+        new Hashtable<String,String>(101);
+    static {
+        defaultMap.put("txt", "text/plain");
+        defaultMap.put("css", "text/css");
+        defaultMap.put("html","text/html");
+        defaultMap.put("htm", "text/html");
+        defaultMap.put("gif", "image/gif");
+        defaultMap.put("jpg", "image/jpeg");
+        defaultMap.put("jpe", "image/jpeg");
+        defaultMap.put("jpeg", "image/jpeg");
+        defaultMap.put("png", "image/png");
+                defaultMap.put("java", "text/plain");
+        defaultMap.put("body", "text/html");
+        defaultMap.put("rtx", "text/richtext");
+        defaultMap.put("tsv", "text/tab-separated-values");
+        defaultMap.put("etx", "text/x-setext");
+        defaultMap.put("ps", "application/x-postscript");
+        defaultMap.put("class", "application/java");
+        defaultMap.put("csh", "application/x-csh");
+        defaultMap.put("sh", "application/x-sh");
+        defaultMap.put("tcl", "application/x-tcl");
+        defaultMap.put("tex", "application/x-tex");
+        defaultMap.put("texinfo", "application/x-texinfo");
+        defaultMap.put("texi", "application/x-texinfo");
+        defaultMap.put("t", "application/x-troff");
+        defaultMap.put("tr", "application/x-troff");
+        defaultMap.put("roff", "application/x-troff");
+        defaultMap.put("man", "application/x-troff-man");
+        defaultMap.put("me", "application/x-troff-me");
+        defaultMap.put("ms", "application/x-wais-source");
+        defaultMap.put("src", "application/x-wais-source");
+        defaultMap.put("zip", "application/zip");
+        defaultMap.put("bcpio", "application/x-bcpio");
+        defaultMap.put("cpio", "application/x-cpio");
+        defaultMap.put("gtar", "application/x-gtar");
+        defaultMap.put("shar", "application/x-shar");
+        defaultMap.put("sv4cpio", "application/x-sv4cpio");
+        defaultMap.put("sv4crc", "application/x-sv4crc");
+        defaultMap.put("tar", "application/x-tar");
+        defaultMap.put("ustar", "application/x-ustar");
+        defaultMap.put("dvi", "application/x-dvi");
+        defaultMap.put("hdf", "application/x-hdf");
+        defaultMap.put("latex", "application/x-latex");
+        defaultMap.put("bin", "application/octet-stream");
+        defaultMap.put("oda", "application/oda");
+        defaultMap.put("pdf", "application/pdf");
+        defaultMap.put("ps", "application/postscript");
+        defaultMap.put("eps", "application/postscript");
+        defaultMap.put("ai", "application/postscript");
+        defaultMap.put("rtf", "application/rtf");
+        defaultMap.put("nc", "application/x-netcdf");
+        defaultMap.put("cdf", "application/x-netcdf");
+        defaultMap.put("cer", "application/x-x509-ca-cert");
+        defaultMap.put("exe", "application/octet-stream");
+        defaultMap.put("gz", "application/x-gzip");
+        defaultMap.put("Z", "application/x-compress");
+        defaultMap.put("z", "application/x-compress");
+        defaultMap.put("hqx", "application/mac-binhex40");
+        defaultMap.put("mif", "application/x-mif");
+        defaultMap.put("ief", "image/ief");
+        defaultMap.put("tiff", "image/tiff");
+        defaultMap.put("tif", "image/tiff");
+        defaultMap.put("ras", "image/x-cmu-raster");
+        defaultMap.put("pnm", "image/x-portable-anymap");
+        defaultMap.put("pbm", "image/x-portable-bitmap");
+        defaultMap.put("pgm", "image/x-portable-graymap");
+        defaultMap.put("ppm", "image/x-portable-pixmap");
+        defaultMap.put("rgb", "image/x-rgb");
+        defaultMap.put("xbm", "image/x-xbitmap");
+        defaultMap.put("xpm", "image/x-xpixmap");
+        defaultMap.put("xwd", "image/x-xwindowdump");
+        defaultMap.put("au", "audio/basic");
+        defaultMap.put("snd", "audio/basic");
+        defaultMap.put("aif", "audio/x-aiff");
+        defaultMap.put("aiff", "audio/x-aiff");
+        defaultMap.put("aifc", "audio/x-aiff");
+        defaultMap.put("wav", "audio/x-wav");
+        defaultMap.put("mpeg", "video/mpeg");
+        defaultMap.put("mpg", "video/mpeg");
+        defaultMap.put("mpe", "video/mpeg");
+        defaultMap.put("qt", "video/quicktime");
+        defaultMap.put("mov", "video/quicktime");
+        defaultMap.put("avi", "video/x-msvideo");
+        defaultMap.put("movie", "video/x-sgi-movie");
+        defaultMap.put("avx", "video/x-rad-screenplay");
+        defaultMap.put("wrl", "x-world/x-vrml");
+        defaultMap.put("mpv2", "video/mpeg2");
+        
+        /* Add XML related MIMEs */
+        
+        defaultMap.put("xml", "text/xml");
+        defaultMap.put("xsl", "text/xml");        
+        defaultMap.put("svg", "image/svg+xml");
+        defaultMap.put("svgz", "image/svg+xml");
+        defaultMap.put("wbmp", "image/vnd.wap.wbmp");
+        defaultMap.put("wml", "text/vnd.wap.wml");
+        defaultMap.put("wmlc", "application/vnd.wap.wmlc");
+        defaultMap.put("wmls", "text/vnd.wap.wmlscript");
+        defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
+    }
+    
+
+    private Hashtable<String,String> map = new Hashtable<String,String>();
+
+    public void addContentType(String extn, String type) {
+        map.put(extn, type.toLowerCase());
+    }
+
+    public Enumeration getExtensions() {
+        return map.keys();
+    }
+
+    public String getMimeType(String ext) {
+        return getContentTypeFor(ext);
+    }
+    
+    public String getContentType(String extn) {
+        String type = (String)map.get(extn.toLowerCase());
+        if( type == null ) type=(String)defaultMap.get( extn );
+        return type;
+    }
+
+    public void removeContentType(String extn) {
+        map.remove(extn.toLowerCase());
+    }
+
+    /** Get extension of file, without fragment id
+     */
+    public static String getExtension( String fileName ) {
+        // play it safe and get rid of any fragment id
+        // that might be there
+        int length=fileName.length();
+        
+        int newEnd = fileName.lastIndexOf('#');
+        if( newEnd== -1 ) newEnd=length;
+        // Instead of creating a new string.
+        //         if (i != -1) {
+        //             fileName = fileName.substring(0, i);
+        //         }
+        int i = fileName.lastIndexOf('.', newEnd );
+        if (i != -1) {
+             return  fileName.substring(i + 1, newEnd );
+        } else {
+            // no extension, no content type
+            return null;
+        }
+    }
+    
+    public String getContentTypeFor(String fileName) {
+        String extn=getExtension( fileName );
+        if (extn!=null) {
+            return getContentType(extn);
+        } else {
+            // no extension, no content type
+            return null;
+        }
+    }
+
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Range.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/Range.java
new file mode 100644 (file)
index 0000000..6bc1f5f
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  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.lite.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
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/URLEncoder.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/URLEncoder.java
new file mode 100644 (file)
index 0000000..4364aea
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * 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.lite.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+/**
+ *
+ * This class is very similar to the java.net.URLEncoder class.
+ *
+ * Unfortunately, with java.net.URLEncoder there is no way to specify to the 
+ * java.net.URLEncoder which characters should NOT be encoded.
+ *
+ * This code was moved from DefaultServlet.java
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class URLEncoder {
+    protected static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'A', 'B', 'C', 'D', 'E', 'F'};
+
+    //Array containing the safe characters set.
+    protected BitSet safeChars = new BitSet(128);
+
+    public URLEncoder() {
+        for (char i = 'a'; i <= 'z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = 'A'; i <= 'Z'; i++) {
+            addSafeCharacter(i);
+        }
+        for (char i = '0'; i <= '9'; i++) {
+            addSafeCharacter(i);
+        }
+        //safe
+        safeChars.set('$');
+        safeChars.set('-');
+        safeChars.set('_');
+        safeChars.set('.');
+
+        // Dangerous: someone may treat this as " "
+        // RFC1738 does allow it, it's not reserved
+        //    safeChars.set('+');
+        //extra
+        safeChars.set('!');
+        safeChars.set('*');
+        safeChars.set('\'');
+        safeChars.set('(');
+        safeChars.set(')');
+        safeChars.set(',');
+    }
+
+    public void addSafeCharacter( char c ) {
+        safeChars.set( c );
+    }
+
+    public String encodeURL(String path) {
+        return encodeURL(path, "UTF-8", true);
+    }
+
+    public String encodeURL(String path, String enc, boolean allowSlash) {
+        int maxBytesPerChar = 10;
+        
+        StringBuffer rewrittenPath = new StringBuffer(path.length());
+        ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
+        OutputStreamWriter writer = null;
+        try {
+            writer = new OutputStreamWriter(buf, enc);
+        } catch (UnsupportedEncodingException e1) {
+            // shouldn't happen.
+        }
+        
+        for (int i = 0; i < path.length(); i++) {
+            int c = (int) path.charAt(i);
+            if (c < 128 && safeChars.get(c) || allowSlash && c == '/') {
+                rewrittenPath.append((char)c);
+            } else {
+                // convert to external encoding before hex conversion
+                try {
+                    writer.write((char)c);
+                    if (c >= 0xD800 && c <= 0xDBFF) {
+                        if ( (i+1) < path.length()) {
+                            int d = path.charAt(i+1);
+                            if (d >= 0xDC00 && d <= 0xDFFF) {
+                                writer.write((char) d);
+                                i++;
+                            }
+                        }
+                    }
+                    writer.flush();
+                } catch(IOException e) {
+                    buf.reset();
+                    continue;
+                }
+                byte[] ba = buf.toByteArray();
+                for (int j = 0; j < ba.length; j++) {
+                    // Converting each byte in the buffer
+                    byte toEncode = ba[j];
+                    rewrittenPath.append('%');
+                    int low = (int) (toEncode & 0x0f);
+                    int high = (int) ((toEncode & 0xf0) >> 4);
+                    rewrittenPath.append(hexadecimal[high]);
+                    rewrittenPath.append(hexadecimal[low]);
+                }
+                buf.reset();
+            }
+        }
+        return rewrittenPath.toString();
+    }
+    
+    /**
+     * 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 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 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
+     */
+    private 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;
+    }
+    
+}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/lite/util/UrlUtils.java b/modules/tomcat-lite/java/org/apache/tomcat/lite/util/UrlUtils.java
new file mode 100644 (file)
index 0000000..c0d9df0
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  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.lite.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);
+    }
+
+}
index 50ea4e6..4015bf7 100644 (file)
@@ -41,8 +41,11 @@ import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.tomcat.servlets.util.Range;
-import org.apache.tomcat.servlets.util.URLEncoder;
+
+import org.apache.tomcat.lite.util.CopyUtils;
+import org.apache.tomcat.lite.util.Dir2Html;
+import org.apache.tomcat.lite.util.Range;
+import org.apache.tomcat.lite.util.URLEncoder;
 
 /**
  * The default resource-serving servlet for most web applications,
@@ -124,8 +127,6 @@ public class DefaultServlet  extends HttpServlet {
 
 
     // --------------------------------------------------------- Public Methods
-    protected Filesystem fs;
-
     /**
      * Finalize this servlet.
      */
@@ -136,10 +137,6 @@ public class DefaultServlet  extends HttpServlet {
      * Initialize this servlet.
      */
     public void init() throws ServletException {
-        if (fs == null) {
-            // R/O - no write
-            fs = new Filesystem();
-        }
 
         String realPath = getServletContext().getRealPath("/");
         basePath = new File(realPath);
@@ -148,7 +145,7 @@ public class DefaultServlet  extends HttpServlet {
         } catch (IOException e) {
             basePathName = basePath.getAbsolutePath();
         }
-        log("Init fs " + fs + " base: " + basePathName);
+        log("Init default serviet, base: " + basePathName);
         
         // Set our properties from the initialization parameters
         String value = null;
@@ -183,14 +180,6 @@ public class DefaultServlet  extends HttpServlet {
             output = 256;
     }
 
-    public void setFilesystem(Filesystem fs) {
-        this.fs = fs;
-    }
-    
-    public Filesystem getFilesystem() {
-        return fs;
-    }
-    
     public void setBasePath(String s) {
         this.basePathName = s;
         this.basePath = new File(s);
@@ -391,6 +380,64 @@ public class DefaultServlet  extends HttpServlet {
 //        }
     }
     
+    public void renderDir(HttpServletRequest request, 
+            HttpServletResponse response, 
+            File resFile,
+            String fileEncoding,
+            boolean content,
+            String relativePath) throws IOException {
+        
+        String contentType = "text/html;charset=" + fileEncoding;
+
+        ServletOutputStream ostream = null;
+        PrintWriter writer = null;
+        
+        if (content) {
+            // Trying to retrieve the servlet output stream
+            try {
+                ostream = response.getOutputStream();
+            } catch (IllegalStateException e) {
+                // If it fails, we try to get a Writer instead if we're
+                // trying to serve a text file
+                if ( (contentType == null)
+                     || (contentType.startsWith("text")) ) {
+                    writer = response.getWriter();
+                } else {
+                    throw e;
+                }
+            }
+
+        }
+
+        // Set the appropriate output headers
+        response.setContentType(contentType);
+        
+        InputStream renderResult = null;
+
+        if (content) {
+            // Serve the directory browser
+            renderResult =
+                dir2Html.render(request.getContextPath(), resFile, relativePath);
+        }
+
+
+        // Copy the input stream to our output stream (if requested)
+        if (content) {
+            try {
+                response.setBufferSize(output);
+            } catch (IllegalStateException e) {
+                // Silent catch
+            }
+            if (ostream != null) {
+                CopyUtils.copy(renderResult, ostream);
+            } else {
+                CopyUtils.copy(renderResult, writer, fileEncoding);
+            }
+        }
+        
+            
+    }
+    
 
     /**
      * Serve the specified resource, optionally including the data content.
@@ -430,7 +477,7 @@ public class DefaultServlet  extends HttpServlet {
                                    request.getRequestURI());
                 return;
             }
-            dir2Html.renderDir(request, response, resFile, fileEncoding, content,
+            renderDir(request, response, resFile, fileEncoding, content,
                     path);
             
             return;
index 19a54f1..92c61d6 100644 (file)
@@ -18,6 +18,9 @@ package org.apache.tomcat.servlets.file;
 
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -37,10 +40,13 @@ import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
-import org.apache.tomcat.servlets.util.FastHttpDateFormat;
-import org.apache.tomcat.servlets.util.Range;
+import org.apache.tomcat.lite.util.CopyUtils;
+import org.apache.tomcat.lite.util.FastHttpDateFormat;
+import org.apache.tomcat.lite.util.Range;
+import org.apache.tomcat.lite.util.URLEncoder;
+import org.apache.tomcat.lite.util.UrlUtils;
+import org.apache.tomcat.lite.util.XMLWriter;
 import org.apache.tomcat.servlets.util.RequestUtil;
-import org.apache.tomcat.servlets.util.UrlUtils;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -420,7 +426,7 @@ public class WebdavServlet extends DefaultServlet {
      * @param path Path which has to be rewiten
      */
     protected String rewriteUrl(String path) {
-        return urlEncoder.encode( path );
+        return urlEncoder.encodeURL( path );
     }
 
 
@@ -841,7 +847,7 @@ public class WebdavServlet extends DefaultServlet {
 
         try {
             // will override 
-            OutputStream fos = getFilesystem().getOutputStream(resFile.getPath());
+            OutputStream fos = getOut(resFile.getPath());
             CopyUtils.copy(resourceInputStream, fos);
         } catch(IOException e) {
             result = false;
@@ -866,6 +872,7 @@ public class WebdavServlet extends DefaultServlet {
      * Handle a partial PUT.  New content specified in request is appended to
      * existing content in oldRevisionContent (if present). This code does
      * not support simultaneous partial updates to the same resource.
+     * @throws FileNotFoundException 
      */
 //    protected File executePartialPut(HttpServletRequest req, Range range,
 //                                     String path)
@@ -929,6 +936,10 @@ public class WebdavServlet extends DefaultServlet {
 //    }
 
 
+    private OutputStream getOut(String path) throws FileNotFoundException {
+        return new FileOutputStream(path);
+    }
+
     /**
      * COPY Method.
      */
@@ -1044,7 +1055,7 @@ public class WebdavServlet extends DefaultServlet {
         }
 
         // Remove url encoding from destination
-        destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
+        destinationPath = URLEncoder.URLDecode(destinationPath, "UTF8");
 
         destinationPath = removeDestinationPrefix(req, destinationPath);
 
@@ -1222,8 +1233,8 @@ public class WebdavServlet extends DefaultServlet {
         } else {
 
             try {
-                CopyUtils.copy(getFilesystem().getInputStream(object.getPath()), 
-                    getFilesystem().getOutputStream(dest));
+                CopyUtils.copy(new FileInputStream(object.getPath()), 
+                    getOut(dest));
             } catch(IOException ex ) {
                 errorList.put
                 (source,
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Base64.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Base64.java
deleted file mode 100644 (file)
index 1f42286..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- *  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.logging.Level;
-import java.util.logging.Logger;
-
-
-/**
- * This class provides encode/decode for RFC 2045 Base64 as
- * defined by RFC 2045, N. Freed and N. Borenstein.
- * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
- * Part One: Format of Internet Message Bodies. Reference
- * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
- * This class is used by XML Schema binary format validation
- *
- * @author Jeffrey Rodriguez
- * @version $Revision: 467222 $ $Date: 2006-10-23 20:17:11 -0700 (Mon, 23 Oct 2006) $
- */
-
-public final class Base64 {
-
-
-    private static Logger log=
-        Logger.getLogger( Base64.class.getName() );
-    
-    static private final int  BASELENGTH         = 255;
-    static private final int  LOOKUPLENGTH       = 63;
-    static private final int  TWENTYFOURBITGROUP = 24;
-    static private final int  EIGHTBIT           = 8;
-    static private final int  SIXTEENBIT         = 16;
-    static private final int  SIXBIT             = 6;
-    static private final int  FOURBYTE           = 4;
-
-
-    static private final byte PAD               = ( byte ) '=';
-    static private byte [] base64Alphabet       = new byte[BASELENGTH];
-    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH + 1];
-
-    static {
-
-        for (int i = 0; i<BASELENGTH; i++ ) {
-            base64Alphabet[i] = -1;
-        }
-        for ( int i = 'Z'; i >= 'A'; i-- ) {
-            base64Alphabet[i] = (byte) (i-'A');
-        }
-        for ( int i = 'z'; i>= 'a'; i--) {
-            base64Alphabet[i] = (byte) ( i-'a' + 26);
-        }
-
-        for ( int i = '9'; i >= '0'; i--) {
-            base64Alphabet[i] = (byte) (i-'0' + 52);
-        }
-
-        base64Alphabet['+']  = 62;
-        base64Alphabet['/']  = 63;
-        String lookupString = 
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
-            "0123456789+/";
-        
-       for (int i = 0; i < lookupString.length(); i++ ) {
-            lookUpBase64Alphabet[i] = (byte) lookupString.charAt(i);
-       }
-    }
-
-
-    static boolean isBase64( byte octect ) {
-        //shall we ignore white space? JEFF??
-        return(octect == PAD || base64Alphabet[octect] != -1 );
-    }
-
-
-    static boolean isArrayByteBase64( byte[] arrayOctect ) {
-        int length = arrayOctect.length;
-        if ( length == 0 )
-            return false;
-        for ( int i=0; i < length; i++ ) {
-            if ( Base64.isBase64( arrayOctect[i] ) == false)
-                return false;
-        }
-        return true;
-    }
-
-    /**
-     * Encodes hex octects into Base64
-     *
-     * @param binaryData Array containing binaryData
-     * @return Encoded Base64 array
-     */
-    public static byte[] encode( byte[] binaryData ) {
-        int      lengthDataBits    = binaryData.length*EIGHTBIT;
-        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
-        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
-        byte     encodedData[]     = null;
-
-
-        if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
-            encodedData = new byte[ (numberTriplets + 1 )*4  ];
-        else // 16 or 8 bit
-            encodedData = new byte[ numberTriplets*4 ];
-
-        int k=0, l=0, b1=0,b2=0,b3=0;
-
-        int encodedIndex = 0;
-        int dataIndex   = 0;
-        int i           = 0;
-        for ( i = 0; i<numberTriplets; i++ ) {
-
-            dataIndex = i*3;
-            b1 = binaryData[dataIndex] & 0xFF;
-            b2 = binaryData[dataIndex + 1] & 0xFF;
-            b3 = binaryData[dataIndex + 2] & 0xFF;
-
-            l  = (byte)(b2 & 0x0f);
-            k  = (byte)(b1 & 0x03);
-
-            encodedIndex = i*4;
-            encodedData[encodedIndex]   = lookUpBase64Alphabet[ b1 >>2 ];
-            encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
-( k<<4 )];
-            encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
-( b3>>6)];
-            encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
-        }
-
-        // form integral number of 6-bit groups
-        dataIndex    = i*3;
-        encodedIndex = i*4;
-        if (fewerThan24bits == EIGHTBIT ) {
-            b1 = binaryData[dataIndex];
-            k = (byte) ( b1 &0x03 );
-            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
-            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
-            encodedData[encodedIndex + 2] = PAD;
-            encodedData[encodedIndex + 3] = PAD;
-        } else if ( fewerThan24bits == SIXTEENBIT ) {
-
-            b1 = binaryData[dataIndex];
-            b2 = binaryData[dataIndex +1 ];
-            l = ( byte ) ( b2 &0x0f );
-            k = ( byte ) ( b1 &0x03 );
-            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
-            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
-| ( k<<4 )];
-            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
-            encodedData[encodedIndex + 3] = PAD;
-        }
-        return encodedData;
-    }
-
-
-    /**
-     * Decodes Base64 data into octects
-     *
-     * @param base64Data Byte array containing Base64 data
-     * @return Array containind decoded data.
-     */
-    public static byte[] decode( byte[] base64Data ) {
-        int      numberQuadruple    = base64Data.length/FOURBYTE;
-        byte     decodedData[]      = null;
-        byte     b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
-
-        // Throw away anything not in base64Data
-        // Adjust size
-
-        int encodedIndex = 0;
-        int dataIndex    = 0;
-        decodedData      = new byte[ numberQuadruple*3 + 1 ];
-
-        for (int i = 0; i<numberQuadruple; i++ ) {
-            dataIndex = i*4;
-            marker0   = base64Data[dataIndex +2];
-            marker1   = base64Data[dataIndex +3];
-
-            b1 = base64Alphabet[base64Data[dataIndex]];
-            b2 = base64Alphabet[base64Data[dataIndex +1]];
-
-            if ( marker0 != PAD && marker1 != PAD ) {     //No PAD e.g 3cQl
-                b3 = base64Alphabet[ marker0 ];
-                b4 = base64Alphabet[ marker1 ];
-
-                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
-                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
-(b3>>2) & 0xf) );
-                decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
-            } else if ( marker0 == PAD ) {               //Two PAD e.g. 3c[Pad][Pad]
-                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
-                decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
-                decodedData[encodedIndex+2] = (byte) 0;
-            } else if ( marker1 == PAD ) {              //One PAD e.g. 3cQ[Pad]
-                b3 = base64Alphabet[ marker0 ];
-
-                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 );
-                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
-(b3>>2) & 0xf) );
-                decodedData[encodedIndex+2] = (byte)( b3<<6);
-            }
-            encodedIndex += 3;
-        }
-        return decodedData;
-
-    }
-
-    static final int base64[]= {
-       64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
-           52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
-           64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-           15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
-           64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-           41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-           64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
-    };
-
-    public static String base64Decode( String orig ) {
-       char chars[]=orig.toCharArray();
-       StringBuffer sb=new StringBuffer();
-       int i=0;
-
-       int shift = 0;   // # of excess bits stored in accum
-       int acc = 0;
-       
-       for (i=0; i<chars.length; i++) {
-           int v = base64[ chars[i] & 0xFF ];
-           
-           if ( v >= 64 ) {
-               if( chars[i] != '=' )
-                   if (log.isLoggable(Level.INFO))
-                       log.info("Wrong char in base64: " + chars[i]);
-           } else {
-               acc= ( acc << 6 ) | v;
-               shift += 6;
-               if ( shift >= 8 ) {
-                   shift -= 8;
-                   sb.append( (char) ((acc >> shift) & 0xff));
-               }
-           }
-       }
-       return sb.toString();
-    }
-
-
-}
-
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/FastHttpDateFormat.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/FastHttpDateFormat.java
deleted file mode 100644 (file)
index 7c2f905..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- *  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.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Utility class to generate HTTP dates.
- * 
- * @author Remy Maucherat
- */
-public final class FastHttpDateFormat {
-
-
-    // -------------------------------------------------------------- Variables
-
-
-    protected static final int CACHE_SIZE = 
-        Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
-
-    
-    /**
-     * HTTP date format.
-     */
-    protected static final SimpleDateFormat format = 
-        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
-
-
-    /**
-     * The set of SimpleDateFormat formats to use in getDateHeader().
-     */
-    protected static final SimpleDateFormat formats[] = {
-        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
-        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
-        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
-    };
-
-
-    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
-
-
-    /**
-     * GMT timezone - all HTTP dates are on GMT
-     */
-    static {
-
-        format.setTimeZone(gmtZone);
-
-        formats[0].setTimeZone(gmtZone);
-        formats[1].setTimeZone(gmtZone);
-        formats[2].setTimeZone(gmtZone);
-
-    }
-
-
-    /**
-     * Instant on which the currentDate object was generated.
-     */
-    protected static long currentDateGenerated = 0L;
-
-
-    /**
-     * Current formatted date.
-     */
-    protected static String currentDate = null;
-
-
-    /**
-     * Formatter cache.
-     */
-    protected static final ConcurrentHashMap<Long, String> formatCache = 
-        new ConcurrentHashMap<Long, String>(CACHE_SIZE);
-
-
-    /**
-     * Parser cache.
-     */
-    protected static final ConcurrentHashMap<String, Long> parseCache = 
-        new ConcurrentHashMap<String, Long>(CACHE_SIZE);
-
-
-    // --------------------------------------------------------- Public Methods
-
-
-    /**
-     * Get the current date in HTTP format.
-     */
-    public static final String getCurrentDate() {
-
-        long now = System.currentTimeMillis();
-        if ((now - currentDateGenerated) > 1000) {
-            synchronized (format) {
-                if ((now - currentDateGenerated) > 1000) {
-                    currentDateGenerated = now;
-                    currentDate = format.format(new Date(now));
-                }
-            }
-        }
-        return currentDate;
-
-    }
-
-
-    /**
-     * Get the HTTP format of the specified date.
-     */
-    public static final String formatDate
-        (long value, DateFormat threadLocalformat) {
-
-        Long longValue = new Long(value);
-        String cachedDate = formatCache.get(longValue);
-        if (cachedDate != null)
-            return cachedDate;
-
-        String newDate = null;
-        Date dateValue = new Date(value);
-        if (threadLocalformat != null) {
-            newDate = threadLocalformat.format(dateValue);
-            updateFormatCache(longValue, newDate);
-        } else {
-            synchronized (formatCache) {
-                synchronized (format) {
-                    newDate = format.format(dateValue);
-                }
-                updateFormatCache(longValue, newDate);
-            }
-        }
-        return newDate;
-
-    }
-
-
-    /**
-     * Try to parse the given date as a HTTP date.
-     */
-    public static final long parseDate(String value, 
-                                       DateFormat[] threadLocalformats) {
-
-        Long cachedDate = parseCache.get(value);
-        if (cachedDate != null)
-            return cachedDate.longValue();
-
-        Long date = null;
-        if (threadLocalformats != null) {
-            date = internalParseDate(value, threadLocalformats);
-            updateParseCache(value, date);
-        } else {
-            synchronized (parseCache) {
-                date = internalParseDate(value, formats);
-                updateParseCache(value, date);
-            }
-        }
-        if (date == null) {
-            return (-1L);
-        } else {
-            return date.longValue();
-        }
-
-    }
-
-
-    /**
-     * Parse date with given formatters.
-     */
-    private static final Long internalParseDate
-        (String value, DateFormat[] formats) {
-        Date date = null;
-        for (int i = 0; (date == null) && (i < formats.length); i++) {
-            try {
-                date = formats[i].parse(value);
-            } catch (ParseException e) {
-                ;
-            }
-        }
-        if (date == null) {
-            return null;
-        }
-        return new Long(date.getTime());
-    }
-
-
-    /**
-     * Update cache.
-     */
-    private static void updateFormatCache(Long key, String value) {
-        if (value == null) {
-            return;
-        }
-        if (formatCache.size() > CACHE_SIZE) {
-            formatCache.clear();
-        }
-        formatCache.put(key, value);
-    }
-
-
-    /**
-     * Update cache.
-     */
-    private static void updateParseCache(String key, Long value) {
-        if (value == null) {
-            return;
-        }
-        if (parseCache.size() > CACHE_SIZE) {
-            parseCache.clear();
-        }
-        parseCache.put(key, value);
-    }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/LocaleParser.java
deleted file mode 100644 (file)
index bfe3e42..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * 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.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) {
-          StringBuilder sb = new StringBuilder();
-          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);
-
-    }
-
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/MimeMap.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/MimeMap.java
deleted file mode 100644 (file)
index a173195..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- *  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.net.FileNameMap;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-
-/**
- * A mime type map that implements the java.net.FileNameMap interface.
- *
- * @author James Duncan Davidson [duncan@eng.sun.com]
- * @author Jason Hunter [jch@eng.sun.com]
- */
-public class MimeMap implements FileNameMap {
-
-    // Defaults - all of them are "well-known" types,
-    // you can add using normal web.xml.
-    
-    public static Hashtable<String,String> defaultMap =
-        new Hashtable<String,String>(101);
-    static {
-        defaultMap.put("txt", "text/plain");
-        defaultMap.put("css", "text/css");
-        defaultMap.put("html","text/html");
-        defaultMap.put("htm", "text/html");
-        defaultMap.put("gif", "image/gif");
-        defaultMap.put("jpg", "image/jpeg");
-        defaultMap.put("jpe", "image/jpeg");
-        defaultMap.put("jpeg", "image/jpeg");
-        defaultMap.put("png", "image/png");
-                defaultMap.put("java", "text/plain");
-        defaultMap.put("body", "text/html");
-        defaultMap.put("rtx", "text/richtext");
-        defaultMap.put("tsv", "text/tab-separated-values");
-        defaultMap.put("etx", "text/x-setext");
-        defaultMap.put("ps", "application/x-postscript");
-        defaultMap.put("class", "application/java");
-        defaultMap.put("csh", "application/x-csh");
-        defaultMap.put("sh", "application/x-sh");
-        defaultMap.put("tcl", "application/x-tcl");
-        defaultMap.put("tex", "application/x-tex");
-        defaultMap.put("texinfo", "application/x-texinfo");
-        defaultMap.put("texi", "application/x-texinfo");
-        defaultMap.put("t", "application/x-troff");
-        defaultMap.put("tr", "application/x-troff");
-        defaultMap.put("roff", "application/x-troff");
-        defaultMap.put("man", "application/x-troff-man");
-        defaultMap.put("me", "application/x-troff-me");
-        defaultMap.put("ms", "application/x-wais-source");
-        defaultMap.put("src", "application/x-wais-source");
-        defaultMap.put("zip", "application/zip");
-        defaultMap.put("bcpio", "application/x-bcpio");
-        defaultMap.put("cpio", "application/x-cpio");
-        defaultMap.put("gtar", "application/x-gtar");
-        defaultMap.put("shar", "application/x-shar");
-        defaultMap.put("sv4cpio", "application/x-sv4cpio");
-        defaultMap.put("sv4crc", "application/x-sv4crc");
-        defaultMap.put("tar", "application/x-tar");
-        defaultMap.put("ustar", "application/x-ustar");
-        defaultMap.put("dvi", "application/x-dvi");
-        defaultMap.put("hdf", "application/x-hdf");
-        defaultMap.put("latex", "application/x-latex");
-        defaultMap.put("bin", "application/octet-stream");
-        defaultMap.put("oda", "application/oda");
-        defaultMap.put("pdf", "application/pdf");
-        defaultMap.put("ps", "application/postscript");
-        defaultMap.put("eps", "application/postscript");
-        defaultMap.put("ai", "application/postscript");
-        defaultMap.put("rtf", "application/rtf");
-        defaultMap.put("nc", "application/x-netcdf");
-        defaultMap.put("cdf", "application/x-netcdf");
-        defaultMap.put("cer", "application/x-x509-ca-cert");
-        defaultMap.put("exe", "application/octet-stream");
-        defaultMap.put("gz", "application/x-gzip");
-        defaultMap.put("Z", "application/x-compress");
-        defaultMap.put("z", "application/x-compress");
-        defaultMap.put("hqx", "application/mac-binhex40");
-        defaultMap.put("mif", "application/x-mif");
-        defaultMap.put("ief", "image/ief");
-        defaultMap.put("tiff", "image/tiff");
-        defaultMap.put("tif", "image/tiff");
-        defaultMap.put("ras", "image/x-cmu-raster");
-        defaultMap.put("pnm", "image/x-portable-anymap");
-        defaultMap.put("pbm", "image/x-portable-bitmap");
-        defaultMap.put("pgm", "image/x-portable-graymap");
-        defaultMap.put("ppm", "image/x-portable-pixmap");
-        defaultMap.put("rgb", "image/x-rgb");
-        defaultMap.put("xbm", "image/x-xbitmap");
-        defaultMap.put("xpm", "image/x-xpixmap");
-        defaultMap.put("xwd", "image/x-xwindowdump");
-        defaultMap.put("au", "audio/basic");
-        defaultMap.put("snd", "audio/basic");
-        defaultMap.put("aif", "audio/x-aiff");
-        defaultMap.put("aiff", "audio/x-aiff");
-        defaultMap.put("aifc", "audio/x-aiff");
-        defaultMap.put("wav", "audio/x-wav");
-        defaultMap.put("mpeg", "video/mpeg");
-        defaultMap.put("mpg", "video/mpeg");
-        defaultMap.put("mpe", "video/mpeg");
-        defaultMap.put("qt", "video/quicktime");
-        defaultMap.put("mov", "video/quicktime");
-        defaultMap.put("avi", "video/x-msvideo");
-        defaultMap.put("movie", "video/x-sgi-movie");
-        defaultMap.put("avx", "video/x-rad-screenplay");
-        defaultMap.put("wrl", "x-world/x-vrml");
-        defaultMap.put("mpv2", "video/mpeg2");
-        
-        /* Add XML related MIMEs */
-        
-        defaultMap.put("xml", "text/xml");
-        defaultMap.put("xsl", "text/xml");        
-        defaultMap.put("svg", "image/svg+xml");
-        defaultMap.put("svgz", "image/svg+xml");
-        defaultMap.put("wbmp", "image/vnd.wap.wbmp");
-        defaultMap.put("wml", "text/vnd.wap.wml");
-        defaultMap.put("wmlc", "application/vnd.wap.wmlc");
-        defaultMap.put("wmls", "text/vnd.wap.wmlscript");
-        defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
-    }
-    
-
-    private Hashtable<String,String> map = new Hashtable<String,String>();
-
-    public void addContentType(String extn, String type) {
-        map.put(extn, type.toLowerCase());
-    }
-
-    public Enumeration getExtensions() {
-        return map.keys();
-    }
-
-    public String getMimeType(String ext) {
-        return getContentTypeFor(ext);
-    }
-    
-    public String getContentType(String extn) {
-        String type = (String)map.get(extn.toLowerCase());
-        if( type == null ) type=(String)defaultMap.get( extn );
-        return type;
-    }
-
-    public void removeContentType(String extn) {
-        map.remove(extn.toLowerCase());
-    }
-
-    /** Get extension of file, without fragment id
-     */
-    public static String getExtension( String fileName ) {
-        // play it safe and get rid of any fragment id
-        // that might be there
-        int length=fileName.length();
-        
-        int newEnd = fileName.lastIndexOf('#');
-        if( newEnd== -1 ) newEnd=length;
-        // Instead of creating a new string.
-        //         if (i != -1) {
-        //             fileName = fileName.substring(0, i);
-        //         }
-        int i = fileName.lastIndexOf('.', newEnd );
-        if (i != -1) {
-             return  fileName.substring(i + 1, newEnd );
-        } else {
-            // no extension, no content type
-            return null;
-        }
-    }
-    
-    public String getContentTypeFor(String fileName) {
-        String extn=getExtension( fileName );
-        if (extn!=null) {
-            return getContentType(extn);
-        } else {
-            // no extension, no content type
-            return null;
-        }
-    }
-
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/Range.java
deleted file mode 100644 (file)
index 31d6b27..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- *  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.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
index 5135b00..aefc629 100644 (file)
@@ -304,102 +304,22 @@ public final class RequestUtil {
     }
 
 
-    /**
-     * 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) {
+//    public static String URLDecode(byte[] bytes) {
+//        return URLDecode(bytes, null);
+//    }
 
-        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);
 
-    }
 
 
     /**
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/URLEncoder.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/URLEncoder.java
deleted file mode 100644 (file)
index bf15cd9..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.BitSet;
-
-/**
- *
- * This class is very similar to the java.net.URLEncoder class.
- *
- * Unfortunately, with java.net.URLEncoder there is no way to specify to the 
- * java.net.URLEncoder which characters should NOT be encoded.
- *
- * This code was moved from DefaultServlet.java
- *
- * @author Craig R. McClanahan
- * @author Remy Maucherat
- */
-public class URLEncoder {
-    protected static final char[] hexadecimal =
-    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-     'A', 'B', 'C', 'D', 'E', 'F'};
-
-    //Array containing the safe characters set.
-    protected BitSet safeCharacters = new BitSet(256);
-
-    public URLEncoder() {
-        for (char i = 'a'; i <= 'z'; i++) {
-            addSafeCharacter(i);
-        }
-        for (char i = 'A'; i <= 'Z'; i++) {
-            addSafeCharacter(i);
-        }
-        for (char i = '0'; i <= '9'; i++) {
-            addSafeCharacter(i);
-        }
-    }
-
-    public void addSafeCharacter( char c ) {
-       safeCharacters.set( c );
-    }
-
-    public String encode( String path ) {
-        int maxBytesPerChar = 10;
-        int caseDiff = ('a' - 'A');
-        StringBuffer rewrittenPath = new StringBuffer(path.length());
-        ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
-        OutputStreamWriter writer = null;
-        try {
-            writer = new OutputStreamWriter(buf, "UTF8");
-        } catch (Exception e) {
-            e.printStackTrace();
-            writer = new OutputStreamWriter(buf);
-        }
-
-        for (int i = 0; i < path.length(); i++) {
-            int c = (int) path.charAt(i);
-            if (safeCharacters.get(c)) {
-                rewrittenPath.append((char)c);
-            } else {
-                // convert to external encoding before hex conversion
-                try {
-                    writer.write((char)c);
-                    writer.flush();
-                } catch(IOException e) {
-                    buf.reset();
-                    continue;
-                }
-                byte[] ba = buf.toByteArray();
-                for (int j = 0; j < ba.length; j++) {
-                    // Converting each byte in the buffer
-                    byte toEncode = ba[j];
-                    rewrittenPath.append('%');
-                    int low = (int) (toEncode & 0x0f);
-                    int high = (int) ((toEncode & 0xf0) >> 4);
-                    rewrittenPath.append(hexadecimal[high]);
-                    rewrittenPath.append(hexadecimal[low]);
-                }
-                buf.reset();
-            }
-        }
-        return rewrittenPath.toString();
-    }
-}
diff --git a/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java b/modules/tomcat-lite/java/org/apache/tomcat/servlets/util/UrlUtils.java
deleted file mode 100644 (file)
index f968fb8..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *  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;
-
-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);
-    }
-
-}
index e165383..4eefbf7 100644 (file)
@@ -28,7 +28,6 @@ public class TomcatLiteCoyoteTest extends TestCase {
                 tomcat.setPort(8885);
                 tomcat.setBaseDir("../../output/build/webapps");
                 
-                tomcat.addWebapp("/examples", "examples");
                 tomcat.addWebapp("/", "ROOT");
 
                 
@@ -67,11 +66,11 @@ public class TomcatLiteCoyoteTest extends TestCase {
     public void testSimple() throws IOException {
         HttpConnector clientCon = DefaultHttpConnector.get();
         HttpChannel ch = clientCon.get("localhost", 8885);
-        ch.getRequest().setRequestURI("/examples/servlets/servlet/HelloWorldExample");
+        ch.getRequest().setRequestURI("/index.html");
         ch.getRequest().send();
         BBuffer res = ch.readAll(null, 0);
         
-        assertTrue(res.toString().indexOf("<title>Hello World!</title>") >= 0);
+        assertTrue(res.toString(), res.toString().indexOf("<title>Apache Tomcat</title>") >= 0);
     }
     
     
index 41126d8..005f249 100644 (file)
@@ -7,9 +7,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
-import org.apache.tomcat.integration.jmx.JMXProxyServlet;
-import org.apache.tomcat.integration.jmx.JmxObjectManagerSpi;
 import org.apache.tomcat.integration.jmx.UJmxHandler;
 import org.apache.tomcat.integration.jmx.UJmxObjectManagerSpi;
 import org.apache.tomcat.integration.simple.Main;
@@ -29,13 +29,12 @@ import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
 import org.apache.tomcat.lite.http.services.EchoCallback;
 import org.apache.tomcat.lite.http.services.SleepCallback;
 import org.apache.tomcat.lite.io.BBuffer;
+import org.apache.tomcat.lite.io.IOConnector;
 import org.apache.tomcat.lite.io.SocketConnector;
 import org.apache.tomcat.lite.io.SslConnector;
 import org.apache.tomcat.lite.proxy.HttpProxyService;
 import org.apache.tomcat.lite.proxy.StaticContentService;
 import org.apache.tomcat.lite.service.IOStatus;
-import org.apache.tomcat.lite.servlet.ServletConfigImpl;
-import org.apache.tomcat.util.buf.ByteChunk;
 
 /**
  * Server with lost of test servlets.
@@ -54,15 +53,20 @@ public class TestMain {
     
     private SimpleObjectManager om;
 
+    private boolean init = false;
+    
     private SocketConnector serverCon = new SocketConnector();
     
     private HttpConnector testClient = DefaultHttpConnector.get();
     private HttpConnector testServer = new HttpConnector(serverCon);
     private HttpConnector testProxy = new HttpConnector(serverCon);
-
+    private HttpConnector sslServer;
+    
     private HttpProxyService proxy;
 
     UJmxObjectManagerSpi jmx = new UJmxObjectManagerSpi();
+
+    private IOConnector sslCon;
         
     public static TestMain shared() {
         if (defaultServer == null) {
@@ -118,8 +122,6 @@ public class TestMain {
         });
 
         d.addWrapper(mCtx, "/ujmx", new UJmxHandler(jmx));
-        d.addWrapper(mCtx, "/jmx", 
-                new ServletConfigImpl(new JMXProxyServlet()));
     }
 
     public void run() {
@@ -142,10 +144,11 @@ public class TestMain {
         return 8443;
     }
     
-    protected void startAll(int basePort) throws IOException {
+    protected synchronized void startAll(int basePort) throws IOException {
         int port = basePort + 903;
-        if (proxy == null) {
-
+        if (!init) {
+            init = true;
+            
             proxy = new HttpProxyService()
                 .withHttpClient(testClient);
             testProxy.setPort(port);
@@ -169,18 +172,21 @@ public class TestMain {
                 e.printStackTrace();
             }
             
-            SslConnector sslCon = new SslConnector()
+            sslCon = new SslConnector()
                 .setKeysResource("org/apache/tomcat/lite/http/test.keystore", 
                     "changeit");
-            HttpConnector sslServer = new HttpConnector(sslCon);
+            sslServer = new HttpConnector(sslCon);
             initTestCallback(sslServer.getDispatcher());            
             sslServer.setPort(basePort + 443);
             sslServer.start();
+
+//            System.setProperty("javax.net.debug", "ssl");
             
-//          testProxy.setDebugHttp(true);
-//          testProxy.setDebug(true);
-//          testClient.setDebug(true);
-//          testClient.setDebugHttp(true);
+//            Logger.getLogger("SSL").setLevel(Level.FINEST);
+//            testProxy.setDebugHttp(true);
+//            testProxy.setDebug(true);
+//            testClient.setDebug(true);
+//            testClient.setDebugHttp(true);
 //            testServer.setDebugHttp(true);
 //            testServer.setDebug(true);
 //            sslServer.setDebug(true);
@@ -204,9 +210,17 @@ public class TestMain {
     public void bindConnector(HttpConnector con, final String base) {
         om.bind("HttpConnector-" + base, con);
         om.bind("HttpConnectionPool-" + base, con.cpool);
-        SocketConnector sc = (SocketConnector) con.getIOConnector();
-        om.bind("NioThread-" + base, sc.getSelector());
-
+        IOConnector io = con.getIOConnector();
+        int ioLevel = 0;
+        while (io != null) {
+            om.bind("IOConnector-" + (ioLevel++) + "-" + base, io);
+            if (io instanceof SocketConnector) {
+                om.bind("NioThread-" + base, 
+                        ((SocketConnector) io).getSelector());
+                
+            }
+            io = io.getNet();
+        }
         con.cpool.setEvents(new HttpConnectionPool.HttpConnectionPoolEvents() {
 
             @Override
@@ -251,13 +265,12 @@ public class TestMain {
         if (om == null) {
             om = new SimpleObjectManager();
         }
-        // All objects visible in JMX via util.registry
-        // ( optional dependency )
-        om.register(new JmxObjectManagerSpi());
         om.register(jmx);
         
-        
+        // Additional settings, via spring-like config file
         om.loadResource(cfgFile);
+        
+        // initialization - using runnables
         String run = (String) om.getProperty("RUN");
         String[] runNames = run == null ? new String[] {} : run.split(",");
         for (String name: runNames) {
@@ -271,6 +284,7 @@ public class TestMain {
         bindConnector(testServer, "TestServer");
         bindConnector(testClient, "Client");
         bindConnector(testProxy, "Proxy");
+        bindConnector(sslServer, "Https");
         
     }
     
@@ -291,14 +305,14 @@ public class TestMain {
         return out;
     }
     
-    public static ByteChunk getUrl(String path) throws IOException {
-        ByteChunk out = new ByteChunk();
+    public static BBuffer getUrl(String path) throws IOException {
+        BBuffer out = BBuffer.allocate();
         getUrl(path, out);
         return out;
     }
 
     public static HttpURLConnection getUrl(String path, 
-                             ByteChunk out) throws IOException {
+                             BBuffer out) throws IOException {
         URL url = new URL(path);
         HttpURLConnection connection = 
             (HttpURLConnection) url.openConnection();
index ed87256..25060c1 100644 (file)
 
 package org.apache.tomcat.lite.http;
 
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
 import junit.framework.TestCase;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.tomcat.lite.TestMain;
 import org.apache.tomcat.lite.io.BBuffer;
 import org.apache.tomcat.lite.io.SslConnector;
-import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.lite.util.Base64;
 
 public class HttpsTest extends TestCase {
     
     static int port = 8443;
     final HttpConnector httpClient = TestMain.shared().getClient();
     
-    public void setUp() {
-        Logger.getLogger("SSL").setLevel(Level.FINEST);
-    }
-
     public void testSimpleClient() throws Exception {
         checkResponse(httpClient);
     }
     
-    
     public void testSimpleServer() throws Exception {
-        ByteChunk res = TestMain.getUrl("https://localhost:8443/hello");
+        BBuffer res = TestMain.getUrl("https://localhost:8443/hello");
         assertTrue(res.toString().indexOf("Hello") >= 0);
     }       
 
@@ -52,7 +43,7 @@ public class HttpsTest extends TestCase {
         HttpRequest ch = httpCon.request("localhost", port).setSecure(true);
         
         ch.setRequestURI("/hello");
-        ch.setProtocol("HTTP/1.0");
+        ch.setProtocol("HTTP/1.0"); // to force close
         ch.send();
         BBuffer res = ch.readAll();
         
@@ -60,7 +51,7 @@ public class HttpsTest extends TestCase {
     }    
     
     public void testSimpleClient20() throws Exception {
-        for (int i = 0; i < 20; i++) {
+        for (int i = 0; i < 10; i++) {
             checkResponse(httpClient);
         }
     }
@@ -98,7 +89,7 @@ public class HttpsTest extends TestCase {
         TestMain.shared().initTestCallback(con.getDispatcher());
         con.start();
         
-        ByteChunk res = TestMain.getUrl("https://localhost:8444" + 
+        BBuffer res = TestMain.getUrl("https://localhost:8444" + 
             "/hello");
         assertTrue(res.toString().indexOf("Hello") >= 0);
         
index 923a6ae..380f805 100644 (file)
@@ -39,7 +39,8 @@ public class LiveHttp1Test extends TestCase {
         // DefaultHttpConnector.get().setDebugHttp(true);
         TestMain.getTestServer();
 
-        httpClient = DefaultHttpConnector.get().request("localhost", clientPort);
+        httpClient = DefaultHttpConnector.get().request("localhost", 
+                clientPort);
 
         bodyRecvBuffer.recycle();
     }
index 8c51fb8..1ee5e9b 100644 (file)
@@ -5,17 +5,24 @@ package org.apache.tomcat.lite.http;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import junit.framework.TestCase;
 
 import org.apache.tomcat.lite.TestMain;
 import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
 import org.apache.tomcat.lite.io.IOBuffer;
+import org.apache.tomcat.lite.io.SocketConnector;
 
 public class SpdyTest extends TestCase {
     HttpConnector http11Con = TestMain.shared().getClient();
     
-    static HttpConnector spdyCon = DefaultHttpConnector.get();
+    static HttpConnector spdyCon = 
+        new HttpConnector(new SocketConnector());
+
+    static HttpConnector spdyConSsl = 
+        new HttpConnector(new SocketConnector());
     
     HttpConnector memSpdyCon = new HttpConnector(null);
     
@@ -23,6 +30,30 @@ public class SpdyTest extends TestCase {
         HttpRequest req = 
             spdyCon.request("http://localhost:8802/echo/test1");
         
+        // Force SPDY - no negotiation
+        req.setProtocol("SPDY/1.0");
+        
+        HttpResponse res = req.waitResponse();
+        
+        assertEquals(200, res.getStatus());
+        //assertEquals("", res.getHeader(""));
+        
+        BufferedReader reader = res.getReader();
+        String line1 = reader.readLine();
+        //assertEquals("", line1);        
+    }
+    
+    public void testSslClient() throws IOException {
+        
+        HttpRequest req = 
+            spdyConSsl.request("http://localhost:8443/echo/test1");
+        // Enable SSL for the connection.
+        // TODO: this must be done on the first request, all will be 
+        // encrypted.
+        req.setSecure(true);
+        // Force SPDY - no negotiation
+        req.setProtocol("SPDY/1.0");
+        
         HttpResponse res = req.waitResponse();
         
         assertEquals(200, res.getStatus());
@@ -32,11 +63,12 @@ public class SpdyTest extends TestCase {
         String line1 = reader.readLine();
         //assertEquals("", line1);        
     }
+
     
     // Initial frame generated by Chrome
     public void testParse() throws IOException {
             InputStream is = 
-            getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreq0");
+            getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreq0.bin");
         
         IOBuffer iob = new IOBuffer();
         iob.append(is);
@@ -65,7 +97,7 @@ public class SpdyTest extends TestCase {
     // Initial frame generated by Chrome
     public void testParseCompressed() throws IOException {
         InputStream is = 
-            getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreqCompressed");
+            getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreqCompressed.bin");
         
         IOBuffer iob = new IOBuffer();
         iob.append(is);
index 1304038..a2bd28d 100644 (file)
 package org.apache.tomcat.lite.load;
 
 
-import java.io.File;
 import java.io.IOException;
-import java.lang.management.ManagementFactory;
 import java.net.HttpURLConnection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanException;
-import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-import javax.management.ReflectionException;
-
 import junit.framework.TestCase;
 
 import org.apache.tomcat.lite.TestMain;
@@ -39,8 +30,8 @@ import org.apache.tomcat.lite.http.HttpChannel;
 import org.apache.tomcat.lite.http.HttpConnector;
 import org.apache.tomcat.lite.http.HttpRequest;
 import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
+import org.apache.tomcat.lite.io.BBuffer;
 import org.apache.tomcat.lite.io.SocketConnector;
-import org.apache.tomcat.util.buf.ByteChunk;
 
 /*
   Notes on memory use ( from heap dumps ): 
@@ -81,11 +72,9 @@ public class LiveHttpThreadedTest extends TestCase {
         new HttpConnector(new SocketConnector());
     
     ThreadRunner tr;
-    static MBeanServer server;
-    static boolean dumpHeap = false;
+    static boolean dumpHeap = true;
     
     AtomicInteger ok = new AtomicInteger();
-    Object lock = new Object();
     int reqCnt;
 
     Map<HttpRequest, HttpRequest> active = new HashMap();
@@ -95,63 +84,69 @@ public class LiveHttpThreadedTest extends TestCase {
     }
     
     public void test1000Async() throws Exception {
-        try {
-            asyncRequest(10, 100, false, clientCon);
-        } finally {
-            dumpHeap("heapAsync.bin");
-        }
+//        try {
+            asyncRequest(10, 100, false, false, clientCon, "AsyncHttp");
+//          } finally {
+//          dumpHeap("heapAsync.bin");
+//      }
 
     }
 
     public void test10000Async() throws Exception {
-        try {
-            asyncRequest(20, 500, false, clientCon);
-        } finally {
-            dumpHeap("heapAsyncBig.bin");
-        }
+        asyncRequest(20, 500, false, false, clientCon, "AsyncHttp");
     }
 
-    public void test1000AsyncSpdy() throws Exception {
-        try {
-            asyncRequest(10, 100, true, spdyClient);
-        } finally {
-            dumpHeap("heapSpdy1000.bin");
-        }
 
+    public void test1000AsyncSsl() throws Exception {
+        asyncRequest(20, 50, false, true, clientCon, "AsyncHttpSsl");
+    }
+
+    public void test10000AsyncSsl() throws Exception {
+        asyncRequest(20, 500, false, true, clientCon, "AsyncHttpSsl");
+    }
+
+    public void test1000AsyncSpdy() throws Exception {
+        asyncRequest(10, 100, true, false, spdyClient, "AsyncSpdy");
     }
 
     public void test10000AsyncSpdy() throws Exception {
-        try {
-            asyncRequest(20, 500, true, spdyClient);
-        } finally {
-            dumpHeap("heapSpdy10000.bin");
-        }
+        asyncRequest(20, 500, true, false, spdyClient, "AsyncSpdy");
     }
 
     public void test1000AsyncSpdyComp() throws Exception {
-        try {
-            asyncRequest(10, 100, true, spdyClientCompress);
-        } finally {
-            dumpHeap("heapSpdy1000Comp.bin");
-        }
-
+            asyncRequest(10, 100, true, false, spdyClientCompress, "AsyncSpdyComp");
     }
 
     public void test10000AsyncSpdyComp() throws Exception {
-        try {
-            asyncRequest(20, 500, true, spdyClientCompress);
-        } finally {
-            dumpHeap("heapSpdy10000.bin");
-        }
+        asyncRequest(20, 500, true, false, spdyClientCompress, "AsyncSpdyComp");
     }
 
-    public void asyncRequest(int thr, int perthr, 
-            final boolean spdy, final HttpConnector clientCon) throws Exception {
+    public void test1000AsyncSpdySsl() throws Exception {
+        asyncRequest(10, 100, true, true, spdyClient, "AsyncSpdySsl");
+    }
+
+    public void test1000AsyncSpdyCompSsl() throws Exception {
+        asyncRequest(10, 100, true, true, spdyClientCompress, "AsyncSpdyCompSsl");
+    }
+
+    public void test10000AsyncSpdyCompSsl() throws Exception {
+        asyncRequest(20, 500, true, true, spdyClientCompress, "AsyncSpdyCompSsl");
+    }
+
+    Object thrlock = new Object();
+    Object lock = new Object();
+    
+    public void asyncRequest(final int thr, int perthr, 
+            final boolean spdy, final boolean ssl, 
+            final HttpConnector clientCon, String test) throws Exception {
+        clientCon.getConnectionPool().clear();
         reqCnt = thr * perthr;
         long t0 = System.currentTimeMillis();
+
         tr = new ThreadRunner(thr, perthr) {
             public void makeRequest(int i) throws Exception {
-                HttpRequest cstate = clientCon.request("localhost", 8802);
+                HttpRequest cstate = clientCon.request("localhost", 
+                        ssl ? 8443 : 8802);
                 synchronized (active) {
                     active.put(cstate, cstate);
                 }
@@ -160,39 +155,96 @@ public class LiveHttpThreadedTest extends TestCase {
                     // a negotiation.
                     cstate.setProtocol("SPDY/1.0");
                 }
+                if (ssl) {
+                    cstate.setSecure(true);
+                }
                 cstate.requestURI().set("/hello");
                 cstate.setCompletedCallback(reqCallback);
                 // no body
                 cstate.getBody().close();
-                // Send the request, wait response
-                Thread.currentThread().sleep(20);
+                
                 cstate.send();
+                
+                while (active.size() >= thr) {
+                    synchronized(thrlock) {
+                        thrlock.wait();
+                    }
+                }
             }
         };
         tr.run();
-        assertEquals(0, tr.errors.get());
         synchronized (lock) {
             if (ok.get() < reqCnt) {
                 lock.wait(reqCnt * 100);
             }
         }
+        long time = (System.currentTimeMillis() - t0);
+
+        System.err.println("====== " + test + 
+                " threads: " + thr + ", req: " + 
+                reqCnt + ", sendTime" + tr.time + 
+                ", time: " + time + 
+                ", connections: " + clientCon.getConnectionPool().getSocketCount() +
+                ", avg: " + (time / reqCnt));
+
         assertEquals(reqCnt, ok.get());
-        System.err.println(reqCnt + " Async requests: " + (System.currentTimeMillis() - t0));
+        assertEquals(0, tr.errors.get());
     }
+    
+    RequestCompleted reqCallback = new RequestCompleted() {
+        @Override
+        public void handle(HttpChannel data, Object extraData) 
+        throws IOException {
+            String out = data.getIn().copyAll(null).toString();
+            if (200 != data.getResponse().getStatus()) {
+                System.err.println("Wrong status");
+                tr.errors.incrementAndGet();            
+            } else if (!"Hello world".equals(out)) {
+                tr.errors.incrementAndGet();
+                System.err.println("bad result " + out);
+            }        
+            synchronized (active) {
+                active.remove(data.getRequest());
+            }
+            synchronized (thrlock) {
+                thrlock.notify();                
+            }
+            data.release();
+            int okres = ok.incrementAndGet();
+            if (okres >= reqCnt) {
+                synchronized (lock) {
+                    lock.notify();
+                }
+            }
+        }
+    };
+
+
 
     public void testURLRequest1000() throws Exception {
-        urlRequest(10, 100);
+        urlRequest(10, 100, false, "HttpURLConnection");
     }
 
     public void xtestURLRequest10000() throws Exception {
-        urlRequest(20, 500);
+        urlRequest(20, 500, false, "HttpURLConnection");
+
+    }
+
+    // I can't seem to get 1000 requests to all complete...
+    public void xtestURLRequestSsl100() throws Exception {
+        urlRequest(10, 10, true, "HttpURLConnectionSSL");
+    }
+
+    public void xtestURLRequestSsl10000() throws Exception {
+        urlRequest(20, 500, true, "HttpURLConnectionSSL");
 
     }
 
     /** 
      * HttpURLConnection client against lite.http server.
      */
-    public void urlRequest(int thr, int cnt) throws Exception {
+    public void urlRequest(int thr, int cnt, final boolean ssl, String test) 
+            throws Exception {
         long t0 = System.currentTimeMillis();
 
 
@@ -203,8 +255,11 @@ public class LiveHttpThreadedTest extends TestCase {
 
                 public void makeRequest(int i) throws Exception {
                     try {
-                        ByteChunk out = new ByteChunk();
-                        HttpURLConnection con = TestMain.getUrl("http://localhost:8802/hello", out);
+                        BBuffer out = BBuffer.allocate();
+                        String url = ssl ? "https://localhost:8443/hello" :
+                            "http://localhost:8802/hello";
+                        HttpURLConnection con = 
+                            TestMain.getUrl(url, out);
                         if (con.getResponseCode() != 200) {
                             errors.incrementAndGet();
                         }
@@ -220,62 +275,17 @@ public class LiveHttpThreadedTest extends TestCase {
             };
             tr.run();
             assertEquals(0, tr.errors.get());
+            long time = (System.currentTimeMillis() - t0);
 
-            System.err.println(thr + " threads, " + (thr * cnt) + " total blocking URL requests: " + 
-                    (System.currentTimeMillis() - t0));
-
-            //assertEquals(testServer., actual)
+            System.err.println("====== " + test + " threads: " + thr + ", req: " + 
+                    (thr * cnt) + ", time: " + time + ", avg: " +
+                    (time / (thr * cnt)));
         } finally {
-            dumpHeap("heapURLReq.bin");
+            //dumpHeap("heapURLReq.bin");
         }
     }
 
     // TODO: move to a servlet
-    private void dumpHeap(String file) throws InstanceNotFoundException,
-    MBeanException, ReflectionException, MalformedObjectNameException {
-        if (!dumpHeap) {
-            return;
-        }
-        if (server == null) {
-            server = ManagementFactory.getPlatformMBeanServer();
-
-        }
-        File f1 = new java.io.File(file);
-        if (f1.exists()) {
-            f1.delete();
-        }
-        server.invoke(new ObjectName("com.sun.management:type=HotSpotDiagnostic"),
-                "dumpHeap",
-                new Object[] {file, Boolean.FALSE /* live */}, 
-                new String[] {String.class.getName(), "boolean"});
-    }
-
-
-    RequestCompleted reqCallback = new RequestCompleted() {
-        @Override
-        public void handle(HttpChannel data, Object extraData) 
-        throws IOException {
-            String out = data.getIn().copyAll(null).toString();
-            if (200 != data.getResponse().getStatus()) {
-                System.err.println("Wrong status");
-                tr.errors.incrementAndGet();            
-            }
-            if (!"Hello world".equals(out)) {
-                tr.errors.incrementAndGet();
-                System.err.println("bad result " + out);
-            }        
-            synchronized (active) {
-                active.remove(data.getRequest());
-            }
-            data.release();
-            int okres = ok.incrementAndGet();
-            if (okres >= reqCnt) {
-                synchronized (lock) {
-                    lock.notify();
-                }
-            }
-        }
-    };
 
 
 }
index bbc6a47..4539312 100644 (file)
@@ -44,6 +44,7 @@ public class MicroTest extends TestCase {
             mappingData.recycle();
             mapper.map(host, uri, mappingData);
         }
-        System.out.println("Elapsed:" + (System.currentTimeMillis() - time));        
+        // TODO: asserts
+        //System.out.println("Elapsed:" + (System.currentTimeMillis() - time));        
     }
 }
index 8f61ead..96fea90 100644 (file)
@@ -45,7 +45,6 @@ public class ThreadRunner {
         }
         long t1 = System.currentTimeMillis();
         time = t1 - t0;
-        System.err.println("TimeNB: " + (t1 - t0) + " " + res);
     }
     
     public void makeRequests(int cnt) {
index baade73..184b583 100644 (file)
@@ -1,4 +1,7 @@
-RUN=Log,Socks,TomcatLite
+RUN=JMX,Log,Socks,TomcatLite,JMXHandler
+
+JMX.(class)=org.apache.tomcat.integration.jmx.JmxObjectManagerSpi
+JMXHandler.(class)=org.apache.tomcat.integration.jmx.JmxHandler
 
 Log.(class)=org.apache.tomcat.lite.service.LogConfig
 Log.debug=org.apache.tomcat.lite.http.HttpConnector
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/lite/util/UEncoderTest.java b/modules/tomcat-lite/test/org/apache/tomcat/lite/util/UEncoderTest.java
new file mode 100644 (file)
index 0000000..5048e28
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  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.lite.util;
+
+import junit.framework.TestCase;
+
+public class UEncoderTest extends TestCase {
+    URLEncoder enc=new URLEncoder();
+    
+    /*
+     * 
+     * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
+     * TODO: find the relevant rfc and apache tests and add more 
+     */
+    public void testEncodeURL() {
+
+        String eurl1=enc.encodeURL("test");
+        assertEquals("test", eurl1);
+        
+        eurl1=enc.encodeURL("/test");
+        assertEquals("/test", eurl1);
+
+        // safe ranges
+        eurl1=enc.encodeURL("test$-_.");
+        assertEquals("test$-_.", eurl1);
+
+        eurl1=enc.encodeURL("test$-_.!*'(),");
+        assertEquals("test$-_.!*'(),", eurl1);
+
+        eurl1=enc.encodeURL("//test");
+        assertEquals("//test", eurl1);
+
+        
+    }
+
+}
diff --git a/modules/tomcat-lite/test/org/apache/tomcat/util/buf/UEncoderTest.java b/modules/tomcat-lite/test/org/apache/tomcat/util/buf/UEncoderTest.java
deleted file mode 100644 (file)
index f3f8f40..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *  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.util.buf;
-
-import junit.framework.TestCase;
-
-public class UEncoderTest extends TestCase {
-    UEncoder enc=new UEncoder();
-    
-    /*
-     * 
-     * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
-     * TODO: find the relevant rfc and apache tests and add more 
-     */
-    public void testEncodeURL() {
-
-        String eurl1=enc.encodeURL("test");
-        assertEquals("test", eurl1);
-        
-        eurl1=enc.encodeURL("/test");
-        assertEquals("%2ftest", eurl1);
-
-        // safe ranges
-        eurl1=enc.encodeURL("test$-_.");
-        assertEquals("test$-_.", eurl1);
-
-        eurl1=enc.encodeURL("test$-_.!*'(),");
-        assertEquals("test$-_.!*'(),", eurl1);
-
-        eurl1=enc.encodeURL("//test");
-        assertEquals("%2f%2ftest", eurl1);
-
-        
-    }
-
-}