tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cos...@apache.org
Subject svn commit: r884410 [1/4] - /tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/
Date Thu, 26 Nov 2009 06:35:46 GMT
Author: costin
Date: Thu Nov 26 06:35:43 2009
New Revision: 884410

URL: http://svn.apache.org/viewvc?rev=884410&view=rev
Log:
This is the first draft of the refactored coyote connector. 
Lots of big changes:
- MessageBytes is gone - BBuffer, CBuffer are used as buffers, BBucket/CBucket are used for wrapping bytes
- C2B/B2C are gone - replaced by nio charsets in IOReader/IOWriter. CBuffer is still needed (instead of a StringBuilder ) to have
access to the raw chars and avoid copy
- speaking of copy, IOChannel uses a list of buffers instead of growing a buffer. For proxy we should be able to move 
buffers from in to out.  This works a bit better with the char conversions, etc
- Nio code heavily refactored - I didn't add APR but shouldn't be hard if anyonew wants. 
- Ssl is barely working - needs improvements



Added:
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnectorJavaIo.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/Hex.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOBuffer.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOChannel.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOConnector.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOInputStream.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOOutputStream.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOReader.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/IOWriter.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/MemoryIOConnector.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioChannel.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/NioThread.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketConnector.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SocketIOChannel.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslChannel.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/SslConnector.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/UrlEncoding.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/WrappedException.java   (with props)
    tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/package.html   (with props)

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,40 @@
+/*
+ */
+package org.apache.tomcat.lite.io;
+
+import java.nio.ByteBuffer;
+
+
+
+/** 
+ * Holds raw data. Similar interface with a ByteBuffer in 'channel write'
+ * or 'read mode'. Data is between position and limit - allways.
+ * 
+ * TODO: FileBucket, DirectBufferBucket, CharBucket, ...
+ * 
+ * @author Costin Manolache
+ */
+public interface BBucket {
+    
+    public void release();
+
+    public byte[] array();
+    public int position();
+    public int remaining();
+    public int limit();
+    
+    public boolean hasRemaining();
+    
+    public void position(int newStart);
+
+    /** 
+     * Return a byte buffer, with data between position and limit. 
+     * Changes in the ByteBuffer position will not be reflected 
+     * in the IOBucket.
+     * 
+     * @return
+     */
+    public ByteBuffer getByteBuffer();
+
+
+}
\ No newline at end of file

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBucket.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,1193 @@
+/*
+ *  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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/*
+ * In a server it is very important to be able to operate on
+ * the original byte[] without converting everything to chars.
+ * Some protocols are ASCII only, and some allow different
+ * non-UNICODE encodings. The encoding is not known beforehand,
+ * and can even change during the execution of the protocol.
+ * ( for example a multipart message may have parts with different
+ *  encoding )
+ *
+ * For HTTP it is not very clear how the encoding of RequestURI
+ * and mime values can be determined, but it is a great advantage
+ * to be able to parse the request without converting to string.
+ */
+
+// Renamed from ByteChunk to make it easier to write code using both
+
+/**
+ * This class is used to represent a chunk of bytes, and utilities to manipulate
+ * byte[].
+ * 
+ * The buffer can be modified and used for both input and output.
+ * 
+ * There are 2 modes: The chunk can be associated with a sink - ByteInputChannel
+ * or ByteOutputChannel, which will be used when the buffer is empty ( on input
+ * ) or filled ( on output ). For output, it can also grow. This operating mode
+ * is selected by calling setLimit() or allocate(initial, limit) with limit !=
+ * -1.
+ * 
+ * Various search and append method are defined - similar with String and
+ * StringBuffer, but operating on bytes.
+ * 
+ * This is important because it allows processing the http headers directly on
+ * the received bytes, without converting to chars and Strings until the strings
+ * are needed. In addition, the charset is determined later, from headers or
+ * user code.
+ * 
+ * 
+ * @author dac@sun.com
+ * @author James Todd [gonzo@sun.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class BBuffer implements Cloneable, Serializable, 
+    BBucket {
+    
+    /**
+     * Default encoding used to convert to strings. It should be UTF8, but:
+     * - the servlet API requires 8859_1 as default
+     * -  
+     */
+    public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
+
+    // byte[]
+    private byte[] buff;
+
+    private int start = 0;
+
+    private int end;
+
+    private ByteBuffer byteBuffer;
+
+    public static final String CRLF = "\r\n";
+
+    /* Various constant "strings" */
+    public static final byte[] CRLF_BYTES = convertToBytes(BBuffer.CRLF);
+
+    /**
+     * HT.
+     */
+    public static final byte HT = (byte) '\t';
+
+    /**
+     * SP.
+     */
+    public static final byte SP = (byte) ' ';
+
+    /**
+     * LF.
+     */
+    public static final byte LF = (byte) '\n';
+
+    /**
+     * CR.
+     */
+    public static final byte CR = (byte) '\r';
+    
+    //private int useCount;
+    
+    
+    private static final boolean[] isDigit = new boolean[256];
+
+    static Charset UTF8;
+
+    public static final byte A = (byte) 'A';
+
+    public static final byte Z = (byte) 'Z';
+
+    public static final byte a = (byte) 'a';
+
+    public static final byte LC_OFFSET = A - a;
+    private static final byte[] toLower = new byte[256];
+    private static final boolean[] isUpper = new boolean[256];
+
+    static {
+        for (int i = 0; i < 256; i++) {
+            toLower[i] = (byte)i;
+        }
+
+        for (int lc = 'a'; lc <= 'z'; lc++) {
+            int uc = lc + 'A' - 'a';
+            toLower[uc] = (byte)lc;
+            isUpper[uc] = true;
+        }
+    }
+
+    static {
+        for (int d = '0'; d <= '9'; d++) {
+            isDigit[d] = true;
+        }        
+        UTF8 = Charset.forName("UTF-8");
+    }
+
+    public static BBuffer allocate() {
+        return new BBuffer();
+    }
+
+    public static BBuffer allocate(int initial) {
+        return new BBuffer().makeSpace(initial);
+    }
+
+    
+    public static BBuffer allocate(String msg) {
+        BBuffer bc = allocate();
+        byte[] data = msg.getBytes(UTF8);
+        bc.append(data, 0, data.length);
+        return bc;
+    }
+
+    public static BBuffer wrapper(String msg) {
+        BBuffer bc = new IOBucketWrap();
+        byte[] data = msg.getBytes(UTF8);
+        bc.setBytes(data, 0, data.length);
+        return bc;
+    }
+
+    public static BBuffer wrapper() {
+        return new IOBucketWrap();
+    }
+
+    public static BBuffer wrapper(BBuffer bb) {
+        BBuffer res = new IOBucketWrap();
+        res.setBytes(bb.array(), bb.position(), bb.remaining());
+        return res;
+    }
+
+    public static BBuffer wrapper(byte b[], int off, int len) {
+        BBuffer res = new IOBucketWrap();
+        res.setBytes(b, off, len);
+        return res;
+    }
+    
+    public static BBuffer wrapper(BBucket bb, int start, int len) {
+        BBuffer res = new IOBucketWrap();
+        res.setBytes(bb.array(), bb.position() + start, len);
+        return res;
+    }
+
+    /**
+     * Creates a new, uninitialized ByteChunk object.
+     */
+    private BBuffer() {
+    }
+
+    public void append(BBuffer src) {
+        append(src.array(), src.getStart(), src.getLength());
+    }
+
+    /**
+     * Add data to the buffer
+     */
+    public void append(byte src[], int off, int len) {
+        // will grow, up to limit
+        makeSpace(len);
+
+        // assert: makeSpace made enough space
+        System.arraycopy(src, off, buff, end, len);
+        end += len;
+        return;
+    }
+
+    // -------------------- Adding data to the buffer --------------------
+    /**
+     * Append a char, by casting it to byte. This IS NOT intended for unicode.
+     * 
+     * @param c
+     */
+    public void append(char c) {
+        put((byte) c);
+    }
+
+    // -------------------- Removing data from the buffer --------------------
+
+    /**
+     * Returns the message bytes.
+     */
+    @Override
+    public byte[] array() {
+        return buff;
+    }
+    
+    public int capacity() {
+        return buff.length;
+    }
+
+    public boolean equals(BBuffer bb) {
+        return equals(bb.array(), bb.getStart(), bb.getLength());
+    }
+
+    public boolean equals(byte b2[], int off2, int len2) {
+        byte b1[] = buff;
+        if (b1 == null && b2 == null)
+            return true;
+
+        int len = end - start;
+        if (len2 != len || b1 == null || b2 == null)
+            return false;
+
+        int off1 = start;
+
+        while (len-- > 0) {
+            if (b1[off1++] != b2[off2++]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    public boolean equals(char c2[], int off2, int len2) {
+        // XXX works only for enc compatible with ASCII/UTF !!!
+        byte b1[] = buff;
+        if (c2 == null && b1 == null)
+            return true;
+
+        if (b1 == null || c2 == null || end - start != len2) {
+            return false;
+        }
+        int off1 = start;
+        int len = end - start;
+
+        while (len-- > 0) {
+            if ((char) b1[off1++] != c2[off2++]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // -------------------- Conversion and getters --------------------
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * 
+     * @param s
+     *            the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+        // XXX ENCODING - this only works if encoding is UTF8-compat
+        // ( ok for tomcat, where we compare ascii - header names, etc )!!!
+
+        byte[] b = buff;
+        int blen = end - start;
+        if (b == null || blen != s.length()) {
+            return false;
+        }
+        int boff = start;
+        for (int i = 0; i < blen; i++) {
+            if (b[boff++] != s.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * Compares the message bytes to the specified String object.
+     * 
+     * @param s
+     *            the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+        byte[] b = buff;
+        int blen = end - start;
+        if (b == null || blen != s.length()) {
+            return false;
+        }
+        int boff = start;
+        for (int i = 0; i < blen; i++) {
+            if (toLower(b[boff++]) != toLower(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public byte get(int off) {
+        if (start + off >= end) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        return buff[start + off];
+    }
+
+    /** 
+     * Return a byte buffer. Changes in the ByteBuffer position will
+     * not be reflected in the IOBucket
+     * @return
+     */
+    public ByteBuffer getByteBuffer() {
+        if (byteBuffer == null || byteBuffer.array() != buff) {
+            byteBuffer = ByteBuffer.wrap(buff, start, end - start);
+        } else {
+            byteBuffer.position(start);
+            byteBuffer.limit(end);
+        }
+        return byteBuffer;
+    }
+
+    // --------------------
+    public BBuffer getClone() {
+        try {
+            return (BBuffer) this.clone();
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+    
+    public int getEnd() {
+        return end;
+    }
+
+    public int getInt() {
+        return parseInt(buff, start, end - start);
+    }
+    /**
+     * Returns the length of the bytes. XXX need to clean this up
+     */
+    public int getLength() {
+        return end - start;
+    }
+    
+    public long getLong() {
+        return parseLong(buff, start, end - start);
+    }
+
+    public int getOffset() {
+        return start;
+    }
+
+    // -------------------- equals --------------------
+
+    /**
+     * Returns the start offset of the bytes. For output this is the end of the
+     * buffer.
+     */
+    public int getStart() {
+        return start;
+    }
+
+    public ByteBuffer getWriteByteBuffer(int space) {
+        if (space == 0) {
+            space = 16;
+        }
+        makeSpace(space);
+        if (byteBuffer == null || byteBuffer.array() != buff) {
+            byteBuffer = ByteBuffer.wrap(buff, end, buff.length);
+        } else {
+            byteBuffer.position(end);
+            byteBuffer.limit(buff.length);
+        }
+        return byteBuffer;
+    }
+
+    // -------------------- Hash code --------------------
+    public int hashCode() {
+        return hashBytes(buff, start, end - start);
+    }
+
+    public boolean hasLFLF() {
+        return hasLFLF(this);
+    }
+
+    public boolean hasRemaining() {
+        return start < end;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * 
+     * @param s
+     *            the string
+     */
+//    public boolean startsWith(String s) {
+//        // Works only if enc==UTF
+//        byte[] b = buff;
+//        int blen = s.length();
+//        if (b == null || blen > end - start) {
+//            return false;
+//        }
+//        int boff = start;
+//        for (int i = 0; i < blen; i++) {
+//            if (b[boff++] != s.charAt(i)) {
+//                return false;
+//            }
+//        }
+//        return true;
+//    }
+
+    /* Returns true if the message bytes start with the specified byte array */
+//    public boolean startsWith(byte[] b2) {
+//        byte[] b1 = buff;
+//        if (b1 == null && b2 == null) {
+//            return true;
+//        }
+//
+//        int len = end - start;
+//        if (b1 == null || b2 == null || b2.length > len) {
+//            return false;
+//        }
+//        for (int i = start, j = 0; i < end && j < b2.length;) {
+//            if (b1[i++] != b2[j++])
+//                return false;
+//        }
+//        return true;
+//    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * 
+     * @param c
+     *            the character
+     * @param starting
+     *            The start position
+     */
+    public int indexOf(char c, int starting) {
+        int ret = indexOf(buff, start + starting, end, c);
+        return (ret >= start) ? ret - start : -1;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * 
+     * @param s
+     *            the string
+     * @param pos
+     *            The position
+     */
+//    public boolean startsWithIgnoreCase(String s, int pos) {
+//        byte[] b = buff;
+//        int len = s.length();
+//        if (b == null || len + pos > end - start) {
+//            return false;
+//        }
+//        int off = start + pos;
+//        for (int i = 0; i < len; i++) {
+//            if (Ascii.toLower(b[off++]) != Ascii.toLower(s.charAt(i))) {
+//                return false;
+//            }
+//        }
+//        return true;
+//    }
+
+    public int indexOf(String src, int srcOff, int srcLen, int myOff) {
+        char first = src.charAt(srcOff);
+
+        // Look for first char
+        int srcEnd = srcOff + srcLen;
+
+        for (int i = myOff + start; i <= (end - srcLen); i++) {
+            if (buff[i] != first)
+                continue;
+            // found first char, now look for a match
+            int myPos = i + 1;
+            for (int srcPos = srcOff + 1; srcPos < srcEnd;) {
+                if (buff[myPos++] != src.charAt(srcPos++))
+                    break;
+                if (srcPos == srcEnd)
+                    return i - start; // found it
+            }
+        }
+        return -1;
+    }
+
+    // hash ignoring case
+//    public int hashIgnoreCase() {
+//        return hashBytesIC(buff, start, end - start);
+//    }
+
+    public boolean isNull() {
+        return start == end;
+    }
+
+//    private static int hashBytesIC(byte bytes[], int start, int bytesLen) {
+//        int max = start + bytesLen;
+//        byte bb[] = bytes;
+//        int code = 0;
+//        for (int i = start; i < max; i++) {
+//            code = code * 37 + Ascii.toLower(bb[i]);
+//        }
+//        return code;
+//    }
+
+    @Override
+    public int limit() {
+        return end;
+    }
+
+    public void limit(int newEnd) {
+        end = newEnd;
+    }
+
+    /**
+     * Make space for len chars. 
+     * If len is small, allocate a reserve space too.
+     */
+    public BBuffer makeSpace(int count) {
+        byte[] tmp = null;
+
+        int newSize;
+        int desiredSize = end + count;
+
+        if (buff == null) {
+            if (desiredSize < 16)
+                desiredSize = 16; // take a minimum
+            buff = new byte[desiredSize];
+            start = 0;
+            end = 0;
+            return this;
+        }
+
+        // limit < buf.length ( the buffer is already big )
+        // or we already have space XXX
+        if (desiredSize <= buff.length) {
+            return this;
+        }
+        // grow in larger chunks
+        if (desiredSize < 2 * buff.length) {
+            newSize = buff.length * 2;
+            tmp = new byte[newSize];
+        } else {
+            newSize = buff.length * 2 + count;
+            tmp = new byte[newSize];
+        }
+
+        System.arraycopy(buff, start, tmp, 0, end - start);
+        buff = tmp;
+        tmp = null;
+        end = end - start;
+        start = 0;
+        return this;
+    }
+
+//    /**
+//     * Find a character, no side effects.
+//     * 
+//     * @return index of char if found, -1 if not
+//     */
+//    public static int findChars(byte buf[], int start, int end, byte c[]) {
+//        int clen = c.length;
+//        int offset = start;
+//        while (offset < end) {
+//            for (int i = 0; i < clen; i++)
+//                if (buf[offset] == c[i]) {
+//                    return offset;
+//                }
+//            offset++;
+//        }
+//        return -1;
+//    }
+
+//    /**
+//     * Find the first character != c
+//     * 
+//     * @return index of char if found, -1 if not
+//     */
+//    public static int findNotChars(byte buf[], int start, int end, byte c[]) {
+//        int clen = c.length;
+//        int offset = start;
+//        boolean found;
+//
+//        while (offset < end) {
+//            found = true;
+//            for (int i = 0; i < clen; i++) {
+//                if (buf[offset] == c[i]) {
+//                    found = false;
+//                    break;
+//                }
+//            }
+//            if (found) { // buf[offset] != c[0..len]
+//                return offset;
+//            }
+//            offset++;
+//        }
+//        return -1;
+//    }
+
+    @Override
+    public int position() {
+        return start;
+    }
+
+    
+
+    @Override
+    public void position(int newStart) {
+        start = newStart;
+    }
+
+    public void put(byte b) {
+        makeSpace(1);
+        buff[end++] = b;
+    }
+
+    public int read(BBuffer res) {
+        res.setBytes(buff, start, remaining());
+        end = start;
+        return res.remaining();
+    }
+
+    /** 
+     * Read a chunk from is.
+     * 
+     * You don't need to use buffered input stream, we do the 
+     * buffering.
+     */
+    public int read(InputStream is) throws IOException {
+        makeSpace(1024);
+        int res = is.read(buff, end, buff.length - end);
+        if (res > 0) {
+            end += res;
+        }
+        return res;
+    }
+
+    public int readAll(InputStream is) throws IOException {
+        int size = 0;
+        while (true) {
+            int res = read(is);
+            if (res < 0) {
+                return size;
+            }
+            size += res;
+        }
+    }
+
+    public int readByte() {
+        if (start == end) {
+            return -1;
+        }
+        return buff[start++];
+    }
+
+
+    /**
+     *  Read a line - excluding the line terminator, which is consummed as 
+     *  well but not included in the response.
+     *  
+     *  Line can end with CR, LF or CR/LF
+     * 
+     * @param res
+     * @return number of bytes read, or -1 if line ending not found in buffer.
+     */
+    public int readLine(BBuffer res) {
+        int cstart = start;
+        while(start < end) {
+            byte chr = buff[start++];
+            if (chr == CR || chr == LF) {
+                res.setBytes(buff, cstart, start - cstart -1);
+                if (chr == CR) {
+                    if (start < end) {
+                        byte chr2 = buff[start];
+                        if (chr2 == LF) {
+                            start++;
+                        }
+                    }
+                }
+                return res.remaining();
+            }
+        }
+        start = cstart;
+        return -1;
+    }
+    /** 
+     * Consume up to but not including delim.
+     * 
+     */
+    public final int readToDelimOrSpace(byte delim, 
+            BBuffer res) {
+        int resStart = start;
+        while (true) {
+            if (start >= end) {
+                break;
+            }
+            byte chr = buff[start];
+            if (chr == delim || chr == SP || chr == HT) {
+                break;
+            }
+            start++;
+        }
+        res.setBytes(buff, resStart, start - resStart);
+        return res.remaining();
+    }
+
+
+    /** 
+     * Consume all up to the first space or \t, which will be the
+     * first character in the buffer.
+     * 
+     * Consumed data is wrapped in res.
+     */
+    public int readToSpace(BBuffer res) {
+        int resStart = start;
+        while (true) {
+          if (start >= end) {
+              break;
+          }
+          if (buff[start] == SP 
+                  || buff[start] == HT) {
+              break;
+          }
+          start++;
+        }
+        res.setBytes(buff, resStart, start - resStart);
+        return res.remaining();
+    }
+    /**
+     * Resets the message buff to an uninitialized state.
+     */
+    public void recycle() {
+        start = 0;
+        end = 0;
+    }
+    @Override
+    public void release() { 
+//        synchronized (this) {
+//            useCount--;
+//            if (useCount == -1) {
+//                // all slices have been released -
+//                // TODO: callback, return to pool
+//            }
+//        }
+    }
+    public int remaining() {
+        return end - start;
+    }
+
+    public void reset() {
+        buff = null;
+    }
+    
+    // -------------------- Setup --------------------
+    /**
+     * Sets the message bytes to the specified subarray of bytes.
+     * 
+     * @param b
+     *            the ascii bytes
+     * @param off
+     *            the start offset of the bytes
+     * @param len
+     *            the length of the bytes
+     */
+    public void setBytes(byte[] b, int off, int len) {
+        throw new RuntimeException("Can't setBytes on allocated buffer");
+    }
+    
+    public void wrap(BBucket b) {
+        setBytes(b.array(), b.position(), b.remaining());
+    }
+
+    public void wrap(ByteBuffer b) {
+        setBytes(b.array(), b.position(), b.remaining());
+    }
+    
+    protected void setBytesInternal(byte[] b, int off, int len) {
+        buff = b;
+        start = off;
+        end = start + len;
+    }
+    
+//    public final void lowerCase() {
+//        while (start < end) {
+//            byte chr = buff[start];
+//            if ((chr >= A) && (chr <= Z)) {
+//                buff[start] = (byte) (chr - LC_OFFSET);
+//            }
+//            start++;
+//        }
+//    }
+
+    public void setEnd(int i) {
+        end = i;
+    }
+    
+    /**
+     * The old code from MessageBytes, used for setContentLength
+     * and setStatus.
+     * TODO: just use StringBuilder, the method is faster.
+     */
+    public void setLong(long l) {
+        if (array() == null) {
+            makeSpace(20);
+        }
+        long current = l;
+        byte[] buf = array();
+        int start = 0;
+        int end = 0;
+        if (l == 0) {
+            buf[end++] = (byte) '0';
+        } else if (l < 0) {
+            current = -l;
+            buf[end++] = (byte) '-';
+        }
+        while (current > 0) {
+            int digit = (int) (current % 10);
+            current = current / 10;
+            buf[end++] = Hex.HEX[digit];
+        }
+        setOffset(0);
+        setEnd(end);
+        // Inverting buffer
+        end--;
+        if (l < 0) {
+            start++;
+        }
+        while (end > start) {
+            byte temp = buf[start];
+            buf[start] = buf[end];
+            buf[end] = temp;
+            start++;
+            end--;
+        }
+    }
+    
+    public void setOffset(int off) {
+        if (end < off)
+            end = off;
+        start = off;
+    }
+    
+    
+    public int skipEmptyLines() {
+        int resStart = start;
+        while (buff[start] == CR || buff[start] == LF) {
+            start++;
+            if (start == end) {
+                break;
+            }
+        }
+        return start - resStart;
+    }
+    
+    public int skipSpace() {
+        int cstart = start;
+        while (true) {
+          if (start >= end) {
+            return start - cstart;
+          }
+          if ((buff[start] == SP) || (buff[start] == HT)) {
+            start++;
+          } else {
+            return start - cstart;
+          }
+        }
+    }
+
+    public int substract() {
+
+        if ((end - start) == 0) {
+            return -1;
+        }
+
+        return (buff[start++] & 0xFF);
+
+    }
+
+    public int substract(BBuffer src) {
+
+        if ((end - start) == 0) {
+            return -1;
+        }
+
+        int len = getLength();
+        src.append(buff, start, len);
+        start = end;
+        return len;
+
+    }
+    
+    public int substract(byte src[], int off, int len)  {
+
+        if ((end - start) == 0) {
+            return -1;
+        }
+
+        int n = len;
+        if (len > getLength()) {
+            n = getLength();
+        }
+        System.arraycopy(buff, start, src, off, n);
+        start += n;
+        return n;
+
+    }
+    
+    public String toString() {
+        return toString(DEFAULT_CHARACTER_ENCODING);
+    }
+
+    public String toString(String enc) {
+        if (null == buff) {
+            return null;
+        } else if (end - start == 0) {
+            return "";
+        }
+        
+        String strValue = null;
+        try {
+            if (enc == null) {
+                enc = DEFAULT_CHARACTER_ENCODING;
+            }
+            
+            strValue = new String(buff, start, end - start, enc);
+            /*
+             * Does not improve the speed too much on most systems, it's safer
+             * to use the "clasical" new String().
+             * 
+             * Most overhead is in creating char[] and copying, the internal
+             * implementation of new String() is very close to what we do. The
+             * decoder is nice for large buffers and if we don't go to String (
+             * so we can take advantage of reduced GC)
+             * 
+             * // Method is commented out, in: return B2CConverter.decodeString(
+             * enc );
+             */
+        } catch (java.io.UnsupportedEncodingException e) {
+            // Use the platform encoding in that case; the usage of a bad
+            // encoding will have been logged elsewhere already
+            strValue = new String(buff, start, end - start);
+        }
+        return strValue;
+    }
+
+    public void wrapTo(BBuffer res) {
+        res.setBytes(buff, start, remaining());
+    }
+
+    /**
+     * Convert specified String to a byte array. This ONLY WORKS for ascii, UTF
+     * chars will be truncated.
+     * 
+     * @param value
+     *            to convert to byte array
+     * @return the byte array value
+     */
+    public static final byte[] convertToBytes(String value) {
+        byte[] result = new byte[value.length()];
+        for (int i = 0; i < value.length(); i++) {
+            result[i] = (byte) value.charAt(i);
+        }
+        return result;
+    }
+    
+    /**
+     * Find a character, no side effects.
+     * 
+     * @return index of char if found, -1 if not
+     */
+    public static int findChar(byte buf[], int start, int end, char c) {
+        byte b = (byte) c;
+        int offset = start;
+        while (offset < end) {
+            if (buf[offset] == b) {
+                return offset;
+            }
+            offset++;
+        }
+        return -1;
+    }
+    private static int hashBytes(byte buff[], int start, int bytesLen) {
+        int max = start + bytesLen;
+        byte bb[] = buff;
+        int code = 0;
+        for (int i = start; i < max; i++) {
+            code = code * 31 + bb[i];
+            // TODO: if > 0x7F, convert to chars / switch to UTF8
+        }
+        return code;
+    }
+
+    public static boolean hasLFLF(BBucket bucket) {
+        int pos = bucket.position();
+        int lastValid = bucket.limit();
+        byte[] buf = bucket.array();
+        
+        for (int i = pos; i < lastValid; i++) {
+            byte chr = buf[i];
+            if (chr == LF) {
+                if (i + 1 < lastValid && buf[i + 1] == CR) {
+                    // \n\r\n
+                    i++;
+                }
+                if (i + 1 < lastValid && buf[i + 1] == LF) {
+                    return true; // \n\n
+                }
+            } else if (chr == CR) {
+                if (i + 1 < lastValid && buf[i + 1] == CR) {
+                    return true; // \r\r
+                }
+                if (i + 1 < lastValid && buf[i + 1] == LF) {
+                        // \r\n
+                    i++; // skip LF
+                    if (i + 1 < lastValid && buf[i + 1] == CR &&
+                            i + 2 < lastValid && buf[i + 2] == LF) {
+                        i++;
+                        return true;
+                    }
+                }
+                
+            }
+        }
+        return false;
+    }
+
+    public static int indexOf(byte bytes[], int off, int end, char qq) {
+        // Works only for UTF
+        while (off < end) {
+            byte b = bytes[off];
+            if (b == qq)
+                return off;
+            off++;
+        }
+        return -1;
+    }
+
+    /**
+     * Returns true if the specified ASCII character is a digit.
+     */
+
+    public static boolean isDigit(int c) {
+        return isDigit[c & 0xff];
+    }
+
+    /**
+     * Parses an unsigned integer from the specified subarray of bytes.
+     * @param b the bytes to parse
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     * @exception NumberFormatException if the integer format was invalid
+     */
+    public static int parseInt(byte[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        int n = c - '0';
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            n = n * 10 + c - '0';
+        }
+
+        return n;
+    }
+
+    /**
+     * Parses an unsigned long from the specified subarray of bytes.
+     * @param b the bytes to parse
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     * @exception NumberFormatException if the long format was invalid
+     */
+    public static long parseLong(byte[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        long n = c - '0';
+        long m;
+        
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            m = n * 10 + c - '0';
+
+            if (m < n) {
+                // Overflow
+                throw new NumberFormatException();
+            } else {
+                n = m;
+            }
+        }
+
+        return n;
+    }
+
+
+
+    /**
+     * Returns the lower case equivalent of the specified ASCII character.
+     */
+    public static int toLower(int c) {
+        if (c > 0x7f) return c;
+        return toLower[c & 0xff] & 0xff;
+    }
+
+    /**
+     * Returns true if the specified ASCII character is upper case.
+     */
+
+    public static boolean isUpper(int c) {
+        return c < 0x7f && isUpper[c];
+    }
+
+    /**
+     * A slice of a bucket, holding reference to a parent bucket.
+     * 
+     * This is used when a filter splits a bucket - the original
+     * will be replaced with 1 or more slices. When all slices are
+     * released, the parent will also be released.
+     * 
+     * It is not possible to add data.
+     * 
+     * @author Costin Manolache
+     */
+    static class IOBucketWrap extends BBuffer {
+        //IOBucket parent;
+        
+        
+        public BBuffer makeSpace(int count) {
+            throw new RuntimeException("Attempting to change buffer " +
+            		"on a wrapped BBuffer");
+        }
+
+        public void release() { 
+//            if (parent != null) {
+//                parent.release();
+//            }
+        }
+
+        public void setBytes(byte[] b, int off, int len) {
+            super.setBytesInternal(b, off, len);
+        }
+    }
+    
+    
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BBuffer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,381 @@
+/*
+ * 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.io;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.CharBuffer;
+
+
+/**
+ * Cut&pasted from Harmony buffered reader ( apache license ).
+ * Changes:
+ * - additional method to recycle to avoid re-allocating on 
+ * each request.
+ */
+public class BufferedIOReader extends BufferedReader {
+
+    // Not recycled - the buffer is tied to the message/IOReader
+    IOReader in;
+
+    private String enc;
+    boolean closed;
+    private char[] buf;
+    private int marklimit = -1;
+
+    private int count;
+
+    private int markpos = -1;
+
+    private int pos;
+
+    public BufferedIOReader(IOReader realReader) {
+        // we're not using super - we override all methods, but need the 
+        // signature 
+        super(DUMMY_READER, 1); 
+        this.in = realReader;
+        buf = new char[8192];
+    }
+
+    public void recycle() {
+        enc = null;
+        closed = false;
+        
+        if (in != null) {
+            in.recycle();
+        }
+        marklimit = -1;
+        count = 0;
+        markpos = -1;
+        pos = 0;
+    }
+
+    private void checkClosed() throws IOException {
+        if (closed) throw new IOException("closed");
+    }
+    
+    public int read(CharBuffer target) throws IOException {
+        checkClosed();
+        int len = target.remaining();
+        int n = read(target.array(), target.position(), target.remaining());
+        if (n > 0)
+            target.position(target.position() + n);
+        return n;
+    }
+
+
+    public int read(char[] cbuf) throws IOException {
+        return read(cbuf, 0, cbuf.length);
+    }
+
+
+    /**
+     * Closes this reader. This implementation closes the buffered source reader
+     * and releases the buffer. Nothing is done if this reader has already been
+     * closed.
+     * 
+     * @throws IOException
+     *             if an error occurs while closing this reader.
+     */
+    @Override
+    public void close() throws IOException {
+        synchronized (lock) {
+            if (!isClosed()) {
+                in.close();
+                closed = true;
+                // buf remains
+            }
+        }
+    }
+
+    private int fillbuf() throws IOException {
+        if (markpos == -1 || (pos - markpos >= marklimit)) {
+            /* Mark position not set or exceeded readlimit */
+            int result = in.read(buf, 0, buf.length);
+            if (result > 0) {
+                markpos = -1;
+                pos = 0;
+                count = result == -1 ? 0 : result;
+            }
+            return result;
+        }
+        if (markpos == 0 && marklimit > buf.length) {
+            /* Increase buffer size to accommodate the readlimit */
+            int newLength = buf.length * 2;
+            if (newLength > marklimit) {
+                newLength = marklimit;
+            }
+            char[] newbuf = new char[newLength];
+            System.arraycopy(buf, 0, newbuf, 0, buf.length);
+            buf = newbuf;
+        } else if (markpos > 0) {
+            System.arraycopy(buf, markpos, buf, 0, buf.length - markpos);
+        }
+
+        /* Set the new position and mark position */
+        pos -= markpos;
+        count = markpos = 0;
+        int charsread = in.read(buf, pos, buf.length - pos);
+        count = charsread == -1 ? pos : pos + charsread;
+        return charsread;
+    }
+
+    private boolean isClosed() {
+        return closed;
+    }
+
+    @Override
+    public void mark(int readlimit) throws IOException {
+        if (readlimit < 0) {
+            throw new IllegalArgumentException();
+        }
+        synchronized (lock) {
+            checkClosed();
+            marklimit = readlimit;
+            markpos = pos;
+        }
+    }
+
+    @Override
+    public boolean markSupported() {
+        return true;
+    }
+
+    @Override
+    public int read() throws IOException {
+        synchronized (lock) {
+            checkClosed();
+            /* Are there buffered characters available? */
+            if (pos < count || fillbuf() != -1) {
+                return buf[pos++];
+            }
+            markpos = -1;
+            return -1;
+        }
+    }
+
+    @Override
+    public int read(char[] buffer, int offset, int length) throws IOException {
+        synchronized (lock) {
+            checkClosed();
+            if (offset < 0 || offset > buffer.length - length || length < 0) {
+                throw new IndexOutOfBoundsException();
+            }
+            if (length == 0) {
+                return 0;
+            }
+            int required;
+            if (pos < count) {
+                /* There are bytes available in the buffer. */
+                int copylength = count - pos >= length ? length : count - pos;
+                System.arraycopy(buf, pos, buffer, offset, copylength);
+                pos += copylength;
+                if (copylength == length || !in.ready()) {
+                    return copylength;
+                }
+                offset += copylength;
+                required = length - copylength;
+            } else {
+                required = length;
+            }
+
+            while (true) {
+                int read;
+                /*
+                 * If we're not marked and the required size is greater than the
+                 * buffer, simply read the bytes directly bypassing the buffer.
+                 */
+                if (markpos == -1 && required >= buf.length) {
+                    read = in.read(buffer, offset, required);
+                    if (read == -1) {
+                        return required == length ? -1 : length - required;
+                    }
+                } else {
+                    if (fillbuf() == -1) {
+                        return required == length ? -1 : length - required;
+                    }
+                    read = count - pos >= required ? required : count - pos;
+                    System.arraycopy(buf, pos, buffer, offset, read);
+                    pos += read;
+                }
+                required -= read;
+                if (required == 0) {
+                    return length;
+                }
+                if (!in.ready()) {
+                    return length - required;
+                }
+                offset += read;
+            }
+        }
+    }
+
+    /**
+     * Returns the next line of text available from this reader. A line is
+     * represented by zero or more characters followed by {@code '\n'},
+     * {@code '\r'}, {@code "\r\n"} or the end of the reader. The string does
+     * not include the newline sequence.
+     * 
+     * @return the contents of the line or {@code null} if no characters were
+     *         read before the end of the reader has been reached.
+     * @throws IOException
+     *             if this reader is closed or some other I/O error occurs.
+     */
+    public String readLine() throws IOException {
+        synchronized (lock) {
+            checkClosed();
+            /* Are there buffered characters available? */
+            if ((pos >= count) && (fillbuf() == -1)) {
+                return null;
+            }
+            for (int charPos = pos; charPos < count; charPos++) {
+                char ch = buf[charPos];
+                if (ch > '\r') {
+                    continue;
+                }
+                if (ch == '\n') {
+                    String res = new String(buf, pos, charPos - pos);
+                    pos = charPos + 1;
+                    return res;
+                } else if (ch == '\r') {
+                    String res = new String(buf, pos, charPos - pos);
+                    pos = charPos + 1;
+                    if (((pos < count) || (fillbuf() != -1))
+                            && (buf[pos] == '\n')) {
+                        pos++;
+                    }
+                    return res;
+                }
+            }
+
+            char eol = '\0';
+            StringBuilder result = new StringBuilder(80);
+            /* Typical Line Length */
+
+            result.append(buf, pos, count - pos);
+            pos = count;
+            while (true) {
+                /* Are there buffered characters available? */
+                if (pos >= count) {
+                    if (eol == '\n') {
+                        return result.toString();
+                    }
+                    // attempt to fill buffer
+                    if (fillbuf() == -1) {
+                        // characters or null.
+                        return result.length() > 0 || eol != '\0' ? result
+                                .toString() : null;
+                    }
+                }
+                for (int charPos = pos; charPos < count; charPos++) {
+                    if (eol == '\0') {
+                        if ((buf[charPos] == '\n' || buf[charPos] == '\r')) {
+                            eol = buf[charPos];
+                        }
+                    } else if (eol == '\r' && (buf[charPos] == '\n')) {
+                        if (charPos > pos) {
+                            result.append(buf, pos, charPos - pos - 1);
+                        }
+                        pos = charPos + 1;
+                        return result.toString();
+                    } else {
+                        if (charPos > pos) {
+                            result.append(buf, pos, charPos - pos - 1);
+                        }
+                        pos = charPos;
+                        return result.toString();
+                    }
+                }
+                if (eol == '\0') {
+                    result.append(buf, pos, count - pos);
+                } else {
+                    result.append(buf, pos, count - pos - 1);
+                }
+                pos = count;
+            }
+        }
+
+    }
+
+    
+    @Override
+    public boolean ready() throws IOException {
+        synchronized (lock) {
+            checkClosed();
+            return ((count - pos) > 0) || in.ready();
+        }
+    }
+
+    @Override
+    public void reset() throws IOException {
+        synchronized (lock) {
+            checkClosed();
+            if (markpos == -1) {
+                throw new IOException("No mark");
+            }
+            pos = markpos;
+        }
+    }
+
+    @Override
+    public long skip(long amount) throws IOException {
+        if (amount < 0) {
+            throw new IllegalArgumentException();
+        }
+        synchronized (lock) {
+            checkClosed();
+            if (amount < 1) {
+                return 0;
+            }
+            if (count - pos >= amount) {
+                pos += amount;
+                return amount;
+            }
+
+            long read = count - pos;
+            pos = count;
+            while (read < amount) {
+                if (fillbuf() == -1) {
+                    return read;
+                }
+                if (count - pos >= amount - read) {
+                    pos += amount - read;
+                    return amount;
+                }
+                // Couldn't get all the characters, skip what we read
+                read += (count - pos);
+                pos = count;
+            }
+            return amount;
+        }
+    }
+    
+    private static Reader DUMMY_READER = new Reader() {
+        @Override
+        public void close() throws IOException {
+        }
+
+        @Override
+        public int read(char[] cbuf, int off, int len) throws IOException {
+            return 0;
+        }
+    };
+
+    
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/BufferedIOReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,510 @@
+/*
+ */
+package org.apache.tomcat.lite.io;
+
+import java.io.Serializable;
+import java.nio.CharBuffer;
+
+/**
+ * Wraps a char[]. 
+ * 
+ * Doesn't provide any mutation methods. Classes in this package
+ * have access to the buffer, for conversions. 
+ * 
+ * 
+ * @author Costin Manolache
+ */
+public class CBucket implements CharSequence, Comparable, Serializable {
+    protected char value[];
+
+    protected int start;
+
+    protected int end;
+
+    // Reused.
+    protected CharBuffer cb;
+    
+    // cache
+    protected String strValue;
+    protected int hash;
+
+    public CBucket() {
+    }
+
+    /** 
+     * Used by IOWriter for conversion. Will not modify the content.
+     */
+    CharBuffer getNioBuffer() {
+        if (cb == null || cb.array() != value) {
+            cb = CharBuffer.wrap(value, start, end - start);
+        } else {
+            cb.position(start);
+            cb.limit(end);
+        }
+        return cb;
+    }
+    
+    public void recycle() {
+        start = 0;
+        end = 0;
+        value = null;
+        strValue = null;
+        hash = 0;
+    }
+    
+    public String toString() {
+        if (null == value) {
+            return null;
+        } else if (end - start == 0) {
+            return "";
+        }
+        if (strValue == null) {
+            strValue = new String(value, start, end - start);
+        }
+        return strValue;
+    }
+
+    /**
+     * Same as String 
+     */
+    public int hashCode() {
+        int h = hash;
+        if (h == 0) {
+            int off = start;
+            char val[] = value;
+
+            for (int i = start; i < end; i++) {
+                h = 31*h + val[off++];
+            }
+            hash = h;
+        }
+        return h;
+    }
+    
+    public long getLong() {
+        return parseLong(value, start, end - start);
+    }
+
+    public int getInt() {
+        return parseInt(value, start, end - start);
+    }
+
+    public static int parseInt(char[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !BBuffer.isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        int n = c - '0';
+
+        while (--len > 0) {
+            if (!BBuffer.isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            n = n * 10 + c - '0';
+        }
+
+        return n;
+    }
+
+
+    public static long parseLong(char[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !BBuffer.isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        long n = c - '0';
+        long m;
+
+        while (--len > 0) {
+            if (!BBuffer.isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            m = n * 10 + c - '0';
+
+            if (m < n) {
+                // Overflow
+                throw new NumberFormatException();
+            } else {
+                n = m;
+            }
+        }
+
+        return n;
+    }
+
+
+    
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * 
+     * @param s
+     *            the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+        char[] c = value;
+        int len = end - start;
+        if (c == null || len != s.length()) {
+            return false;
+        }
+        int off = start;
+        for (int i = 0; i < len; i++) {
+            if (c[off++] != s.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * 
+     * @param s
+     *            the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+        char[] c = value;
+        int len = end - start;
+        if (c == null || len != s.length()) {
+            return false;
+        }
+        int off = start;
+        for (int i = 0; i < len; i++) {
+            if (BBuffer.toLower(c[off++]) != BBuffer.toLower(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean equals(Object obj) {
+        if (obj instanceof CBuffer) {
+            CBuffer cc = (CBuffer) obj;
+            return equals(cc.value, cc.start, cc.length());
+        } else if (obj instanceof String) {
+            return equals((String)obj);
+        }
+        return false;
+    }
+
+    public boolean equals(char b2[], int off2, int len2) {
+        char b1[] = value;
+        if (b1 == null && b2 == null)
+            return true;
+
+        if (b1 == null || b2 == null || end - start != len2) {
+            return false;
+        }
+        int off1 = start;
+        int len = end - start;
+        while (len-- > 0) {
+            if (b1[off1++] != b2[off2++]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean equals(byte b2[], int off2, int len2) {
+        char b1[] = value;
+        if (b2 == null && b1 == null)
+            return true;
+
+        if (b1 == null || b2 == null || end - start != len2) {
+            return false;
+        }
+        int off1 = start;
+        int len = end - start;
+
+        while (len-- > 0) {
+            if (b1[off1++] != (char) b2[off2++]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * 
+     * @param s
+     *            the string
+     */
+    public boolean startsWith(String s) {
+        char[] c = value;
+        int len = s.length();
+        if (c == null || len > end - start) {
+            return false;
+        }
+        int off = start;
+        for (int i = 0; i < len; i++) {
+            if (c[off++] != s.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * 
+     * @param s
+     *            the string
+     */
+    public boolean startsWithIgnoreCase(String s, int pos) {
+        char[] c = value;
+        int len = s.length();
+        if (c == null || len + pos > end - start) {
+            return false;
+        }
+        int off = start + pos;
+        for (int i = 0; i < len; i++) {
+            if (BBuffer.toLower(c[off++]) != BBuffer.toLower(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int indexOf(char c) {
+        return indexOf(c, start);
+    }
+    
+    public int lastIndexOf(char c) {
+        return lastIndexOf(c, 0, end - start);
+    }
+
+    /** 
+     */
+    public int lastIndexOf(char c, int off, int len) {
+        char[] buf = value;
+        int slash = -1;
+        for (int i = start + len - 1; i >= start + off; i--) {
+            if (buf[i] == c) {
+                slash = i - start;
+                break;
+            }
+        }
+        return slash;
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * 
+     * @param c
+     *            the character
+     */
+    public int indexOf(char c, int starting) {
+        int ret = indexOf(value, start + starting, end, c);
+        return (ret >= start) ? ret - start : -1;
+    }
+
+    public static int indexOf(char chars[], int off, int cend, char qq) {
+        while (off < cend) {
+            char b = chars[off];
+            if (b == qq)
+                return off;
+            off++;
+        }
+        return -1;
+    }
+
+    public int indexOf(String src) {
+        return indexOf(src, 0, src.length(), 0);
+    }
+    
+    public int indexOf(String src, int srcOff, int srcLen, int myOff) {
+        char first = src.charAt(srcOff);
+
+        // Look for first char
+        int srcEnd = srcOff + srcLen;
+
+        for (int i = myOff + start; i <= (end - srcLen); i++) {
+            if (value[i] != first)
+                continue;
+            // found first char, now look for a match
+            int myPos = i + 1;
+            for (int srcPos = srcOff + 1; srcPos < srcEnd;) {
+                if (value[myPos++] != src.charAt(srcPos++))
+                    break;
+                if (srcPos == srcEnd)
+                    return i - start; // found it
+            }
+        }
+        return -1;
+    }
+    
+    public char lastChar() {
+        return value[end - 1];
+    }
+
+    public char charAt(int index) {
+        return value[index + start];
+    }
+    
+    public void wrap(char[] buff, int start, int end) {
+        if (value != null) {
+            throw new RuntimeException("Can wrap only once");
+        }
+        this.value = buff;
+        this.start = start;
+        this.end = end;
+    }
+    
+    public CharSequence subSequence(int sstart, int send) {
+        CBucket seq = new CBucket();
+        seq.wrap(this.value, start + sstart, start + send);
+        return seq;
+    }
+    
+    public int length() {
+        return end - start;
+    }
+
+    @Override
+    public int compareTo(Object o) {
+        // Code based on Harmony
+        if (o instanceof CBuffer) {
+            CBuffer dest = (CBuffer) o;
+            int o1 = start, o2 = dest.start, result;
+            int len = end - start;
+            int destLen = dest.end - dest.start;
+            int fin = (len < destLen ?
+                    end : start + destLen);
+            char[] target = dest.value;
+            while (o1 < fin) {
+                if ((result = value[o1++] - target[o2++]) != 0) {
+                    return result;
+                }
+            }
+            return len - destLen;
+            
+        } else if (o instanceof CharSequence) {
+            CharSequence dest = (CharSequence) o;
+            int o1 = start, o2 = 0, result;
+            int len = end - start;
+            int destLen = dest.length();
+            int fin = (len < destLen ? 
+                    end : start + destLen);
+            while (o1 < fin) {
+                if ((result = value[o1++] - dest.charAt(o2++)) != 0) {
+                    return result;
+                }
+            }
+            return len - destLen;
+            
+        } else {
+            throw new RuntimeException("CompareTo not supported " + o);
+        }
+    }
+
+    /**
+     * Compare given char chunk with String ignoring case.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    public final int compareIgnoreCase(String compareTo) {
+        int result = 0;
+        char[] c = value;
+        int len = compareTo.length();
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (BBuffer.toLower(c[i + start]) > BBuffer.toLower(compareTo.charAt(i))) {
+                result = 1;
+            } else if (BBuffer.toLower(c[i + start]) < BBuffer.toLower(compareTo.charAt(i))) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length() > (end - start)) {
+                result = -1;
+            } else if (compareTo.length() < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Compare given char chunk with String.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    public final int compare(String compareTo) {
+        int result = 0;
+        char[] c = value;
+        int len = compareTo.length();
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (c[i + start] > compareTo.charAt(i)) {
+                result = 1;
+            } else if (c[i + start] < compareTo.charAt(i)) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length() > (end - start)) {
+                result = -1;
+            } else if (compareTo.length() < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+    
+    public int getExtension(CBuffer ext, char slashC, char dotC) {
+        int slash = lastIndexOf(slashC);
+        if (slash < 0) {
+            slash = 0;
+        }
+        int dot = lastIndexOf(dotC, slash, length());
+        if (dot < 0) {
+            return -1;
+        }
+        ext.wrap(this, dot + 1, length());
+        return dot;
+    }
+
+    /**
+     * Find the position of the nth slash, in the given char chunk.
+     */
+    public final int nthSlash(int n) {
+        char[] c = value;
+        int pos = start;
+        int count = 0;
+    
+        while (pos < end) {
+            if ((c[pos++] == '/') && ((++count) == n)) {
+                pos--;
+                break;
+            }
+        }
+    
+        return pos - start;
+    }
+    
+
+    public boolean hasUpper() {
+        for (int i = start; i < end; i++) {
+            char c = value[i];
+            if (c < 0x7F && BBuffer.isUpper(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBucket.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,371 @@
+/*
+ *  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.io;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.CharBuffer;
+
+
+/**
+ * Similar with StringBuilder or StringBuffer, but with access to the 
+ * raw buffer - this avoids copying the data.
+ * 
+ * Utilities to manipluate char chunks. While String is the easiest way to
+ * manipulate chars ( search, substrings, etc), it is known to not be the most
+ * efficient solution - Strings are designed as imutable and secure objects.
+ * 
+ * @author dac@sun.com
+ * @author James Todd [gonzo@sun.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class CBuffer extends CBucket implements Cloneable,   
+        Appendable {
+
+    
+    /**
+     * Creates a new, uninitialized CharChunk object.
+     */
+    public static CBuffer newInstance() {
+        return new CBuffer();
+    }
+
+    private CBuffer() {
+    }
+
+    /**
+     * Resets the message bytes to an uninitialized state.
+     */
+    public void recycle() {
+        dirty();
+        start = 0;
+        end = 0;
+    }
+    
+    /**
+     * Same as String 
+     */
+    public int hashCode() {
+        int h = 0;
+        int off = start;
+        char val[] = value;
+        
+        for (int i = start; i < end; i++) {
+            h = 31*h + val[off++];
+        }
+        return h;
+    }
+
+    public String toString() {
+        if (null == value) {
+            return null;
+        } else if (end - start == 0) {
+            return "";
+        }
+        return new String(value, start, end - start);
+    }
+
+    public void wrap(char[] buff, int start, int end) {
+        dirty();
+        this.value = buff;
+        this.start = start;
+        this.end = end;
+    }
+
+    public void wrap(CBucket buff, int off, int srcEnd) {
+        dirty();
+        this.value = buff.value;
+        this.start = buff.start + off;
+        this.end = this.start + srcEnd - off;
+    }
+    
+    
+    // ----------- Used for IOWriter / conversion ---------
+    
+    public char[] array() {
+        return value;
+    }
+    
+    public int position() {
+        return start;
+    }
+    
+    CharBuffer getAppendCharBuffer() {
+        makeSpace(16);
+        if (cb == null || cb.array() != value) {
+            cb = CharBuffer.wrap(value, end, value.length - end);
+        } else {
+            cb.position(end);
+            cb.limit(value.length);
+        }
+        return cb;        
+    }
+
+    void returnNioBuffer(CharBuffer c) {
+        dirty();
+        start = c.position();
+    }
+    
+    void returnAppendCharBuffer(CharBuffer c) {
+        dirty();
+        end = c.position();
+    }
+
+    // -------- Delete / replace ---------------
+    
+    /** 
+     * 'Delete' all chars after offset.
+     * 
+     * @param offset
+     */
+    public void delete(int offset) {
+       dirty();
+       end = start + offset;
+    }
+
+    // -------------------- Adding data --------------------
+
+    /**
+     * Append methods take start and end - similar with this one.
+     * The source is not modified.
+     */
+    @Override
+    public CBuffer append(CharSequence csq, int astart, int aend)
+            throws IOException {
+        makeSpace(aend - astart);
+        
+        for (int i = astart; i < aend; i++) {
+            value[end++] = csq.charAt(i);
+        }
+        return this;
+    }
+
+    public CBuffer append(char b) {
+        makeSpace(1);
+        value[end++] = b;
+        return this;
+    }
+
+    /**
+     * Add data to the buffer
+     */
+    public CBuffer append(char src[], int srcStart, int srcEnd)  {
+        int len = srcEnd - srcStart;
+        // will grow, up to limit
+        makeSpace(len);
+
+        // assert: makeSpace made enough space
+        System.arraycopy(src, srcStart, value, end, len);
+        end += len;
+        return this;
+    }
+
+    /**
+     * Add data to the buffer
+     */
+    public CBuffer append(StringBuffer sb) {
+        int len = sb.length();
+        makeSpace(len);
+        sb.getChars(0, len, value, end);
+        end += len;
+        return this;
+    }
+
+    /**
+     * Append a string to the buffer
+     */
+    public CBuffer append(String s) {
+        if (s == null) {
+            return this;
+        }
+        append(s, 0, s.length());
+        return this;
+    }
+
+
+    
+    /**
+     * Append a string to the buffer
+     */
+    public CBuffer append(String s, int off, int srcEnd) {
+        if (s == null)
+            return this;
+
+        // will grow, up to limit
+        makeSpace(srcEnd - off);
+
+        // assert: makeSpace made enough space
+        s.getChars(off, srcEnd, value, end);
+        end += srcEnd - off;
+        return this;
+    }
+
+    // TODO: long, int conversions -> get from harmony Long
+    public CBuffer appendInt(int i) {
+        // TODO: copy from harmony StringBuffer
+        append(Integer.toString(i));
+        return this;
+    }
+    
+    
+    public Appendable append(CharSequence cs) {
+        if (cs instanceof CBuffer) {
+            CBuffer src = (CBuffer) cs;
+            append(src.value, src.start, src.end);
+        } else if (cs instanceof String) {
+            append((String) cs);
+        } else {
+            for (int i = 0; i < cs.length(); i++) {
+                append(cs.charAt(i));
+            }
+        }
+        return  this;
+    }
+
+    public CBuffer append(CBuffer src) {
+        append(src.value, src.start, src.end);
+        return  this;
+    }
+
+    
+    public CBuffer append(BBucket bb) {
+        byte[] bbuf = bb.array();
+        int start = bb.position();
+        appendAscii(bbuf, start, bb.remaining());
+        return this;
+    }
+
+    public CBuffer appendAscii(byte[] bbuf, int start, int len) {
+        makeSpace(len);
+        char[] cbuf = value;
+        for (int i = 0; i < len; i++) {
+            cbuf[end + i] = (char) (bbuf[i + start] & 0xff);
+        }
+        end += len;
+        return this;
+    }
+    
+    
+    /**
+     *  Append and advance CharBuffer.
+     * 
+     * @param c
+     */
+    public CBuffer put(CharBuffer c) {
+        append(c.array(), c.position(), c.limit());
+        c.position(c.limit());
+        return this;
+    }
+
+    // ------------- 'set' methods ---------------
+    // equivalent with clean + append
+    
+    public CBuffer set(CBuffer csq, int off, int len) {
+        recycle();
+        append(csq.value, csq.start + off, csq.start + off + len);
+        return this;
+    }
+
+    public CBuffer setChars(char[] c, int off, int len) {
+        recycle();
+        append(c, off, off + len);
+        return this;
+    }
+    
+    public CBuffer set(BBucket bb) {
+        recycle();
+        byte[] bbuf = bb.array();
+        int start = bb.position();
+        appendAscii(bbuf, start, bb.remaining());
+        return this;
+    }
+
+    public CBuffer set(CharSequence csq) {
+        recycle();
+        append(csq);
+        return this;
+    }
+
+    public CBuffer set(CBuffer csq) {
+        recycle();
+        append(csq);
+        return this;
+    }
+
+    public CBuffer set(String csq) {
+        recycle();
+        append(csq);
+        return this;
+    }
+    
+    private void dirty() {
+        hash = 0;
+        strValue = null;
+    }
+    
+    /**
+     * Make space for len chars. If len is small, allocate a reserve space too.
+     * Never grow bigger than limit.
+     */
+    private void makeSpace(int count) {
+        dirty();
+        char[] tmp = null;
+
+        int newSize;
+        int desiredSize = end + count;
+
+        if (value == null) {
+            if (desiredSize < 256)
+                desiredSize = 256; // take a minimum
+            value = new char[desiredSize];
+        }
+
+        // limit < buf.length ( the buffer is already big )
+        // or we already have space XXX
+        if (desiredSize <= value.length) {
+            return;
+        }
+        // grow in larger chunks
+        if (desiredSize < 2 * value.length) {
+            newSize = value.length * 2;
+            tmp = new char[newSize];
+        } else {
+            newSize = value.length * 2 + count;
+            tmp = new char[newSize];
+        }
+
+        System.arraycopy(value, 0, tmp, 0, end);
+        value = tmp;
+        tmp = null;
+    }
+
+    public void toLower() {
+        for (int i = start; i < end; i++) {
+            char c = value[i];
+            if (c < 0x7F) {
+                if (BBuffer.isUpper(c)) {
+                    value[i] = (char) BBuffer.toLower(c);
+                }
+                
+            }
+        }
+    }
+
+    
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/CBuffer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,93 @@
+/*
+ */
+package org.apache.tomcat.lite.io;
+
+import java.io.IOException;
+
+// TODO: dump to a file, hex, etc.
+/**
+ * For debug - will print all bytes that go trough the channel
+ */
+public class DumpChannel extends IOChannel {
+    
+    IOBuffer in = new IOBuffer(this);
+    IOBuffer out = new IOBuffer(this);
+    
+    public DumpChannel(String id) {
+        this.id = id;
+    }
+    
+    public String toString() {
+        return "Dump-" + id + "-" + net.toString();
+    }
+    
+    @Override
+    public void handleReceived(IOChannel ch) throws IOException {
+        processInput(ch.getIn());
+    }
+
+    private void processInput(IOBuffer netIn) throws IOException {
+        boolean any = false;
+        while (true) {
+            BBucket first = netIn.popFirst();
+            if (first == null) {
+                if (netIn.isClosedAndEmpty()) {
+                    out("IN", first, true);
+                    in.close();
+                    any = true;
+                }
+                if (any) {
+                    sendHandleReceivedCallback();
+                }
+                return;
+            }
+            any = true;
+            out("IN", first, false);
+            in.queue(first);
+        }
+    }
+
+    public void startSending() throws IOException {
+        while (true) {
+            BBucket first = out.popFirst();
+            if (first == null) {
+                if (out.isClosedAndEmpty()) {
+                    out("OUT", first, true);
+                    net.getOut().close();
+                }
+                
+                net.startSending();
+                return;
+            }
+            // Dump
+            out("OUT", first, net.getOut().isAppendClosed());
+            net.getOut().queue(first);
+        }
+    }
+    
+    private void out(String dir, BBucket first, boolean closed) {
+        // Dump
+        if (first != null) {
+            String hd = Hex.getHexDump(first.array(), first.position(), 
+                    first.remaining(), true);
+            System.err.println("\n" + dir + ": " + id + " " +
+                    (closed ? "CLS" : "") +
+                    + first.remaining() + "\n" + 
+                    hd);
+        } else {
+            System.err.println("\n" + dir + ": " + id + " " +
+                    (closed ? "CLS" : "") +
+                     "END\n"); 
+        }
+    }
+    
+    @Override
+    public IOBuffer getIn() {
+        return in;
+    }
+
+    @Override
+    public IOBuffer getOut() {
+        return out;
+    }
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/DumpChannel.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java Thu Nov 26 06:35:43 2009
@@ -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.io;
+
+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);
+    }
+
+
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FastHttpDateFormat.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java?rev=884410&view=auto
==============================================================================
--- tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java (added)
+++ tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java Thu Nov 26 06:35:43 2009
@@ -0,0 +1,27 @@
+/*
+ */
+package org.apache.tomcat.lite.io;
+
+
+/**
+ * Initial abstraction for non-blocking File access and to 
+ * support other abstraction. 
+ * 
+ * Tomcat uses JNDI - but that's blocking, does lots of data copy,
+ * is complex.
+ * 
+ * Work in progress..
+ */
+public abstract class FileConnector extends IOConnector {
+
+    public static class FileInfo {
+        String type;
+        int mode;
+        long size;
+        
+    }
+    
+    public abstract boolean isDirectory(String path);
+
+    public abstract boolean isFile(String path);
+}

Propchange: tomcat/trunk/modules/tomcat-lite/java/org/apache/tomcat/lite/io/FileConnector.java
------------------------------------------------------------------------------
    svn:eol-style = native



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


Mime
View raw message