FileDocCategorySizeDatePackage
InternalInputBuffer.javaAPI DocGlassfish v2 API19858Wed May 23 10:27:58 BST 2007org.apache.coyote.http11

InternalInputBuffer

public class InternalInputBuffer extends Object implements org.apache.coyote.InputBuffer
Implementation of InputBuffer which provides HTTP request header parsing as well as transfer decoding.
author
Remy Maucherat

Fields Summary
protected static final org.apache.tomcat.util.res.StringManager
sm
The string manager for this package.
protected org.apache.coyote.Request
request
Associated Coyote request.
protected org.apache.tomcat.util.http.MimeHeaders
headers
Headers of the associated request.
protected boolean
parsingHeader
State.
protected boolean
swallowInput
Swallow input ? (in the case of an expectation)
protected byte[]
buf
Pointer to the current read buffer.
protected int
lastValid
Last valid byte.
protected int
pos
Position in the buffer.
protected int
end
protected InputStream
inputStream
Underlying input stream.
protected org.apache.coyote.InputBuffer
inputStreamInputBuffer
Underlying input buffer.
protected InputFilter[]
filterLibrary
Filter library. Note: Filter[0] is always the "chunked" filter.
protected InputFilter[]
activeFilters
Active filters (in order).
protected int
lastActiveFilter
Index of the last active filter.
private int
stage
The stage we currently are during parsing the request line and the headers
private org.apache.tomcat.util.buf.MessageBytes
headerValue
Constructors Summary
public InternalInputBuffer()
Void constructor.

       ;
    
public InternalInputBuffer(org.apache.coyote.Request request)
Default constructor.

        this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
    
public InternalInputBuffer(org.apache.coyote.Request request, int headerBufferSize)
Alternate constructor.


        this.request = request;
        headers = request.getMimeHeaders();

        buf = new byte[headerBufferSize];

        inputStreamInputBuffer = new InputStreamInputBuffer();

        filterLibrary = new InputFilter[0];
        activeFilters = new InputFilter[0];
        lastActiveFilter = -1;

        parsingHeader = true;
        swallowInput = true;

    
Methods Summary
public voidaddActiveFilter(InputFilter filter)
Add an input filter to the filter library.


        if (lastActiveFilter == -1) {
            filter.setBuffer(inputStreamInputBuffer);
        } else {
            for (int i = 0; i <= lastActiveFilter; i++) {
                if (activeFilters[i] == filter)
                    return;
            }
            filter.setBuffer(activeFilters[lastActiveFilter]);
        }

        activeFilters[++lastActiveFilter] = filter;

        filter.setRequest(request);

    
public voidaddFilter(InputFilter filter)
Add an input filter to the filter library.


        // FIXME: Check for null ?

        InputFilter[] newFilterLibrary = 
            new InputFilter[filterLibrary.length + 1];
        for (int i = 0; i < filterLibrary.length; i++) {
            newFilterLibrary[i] = filterLibrary[i];
        }
        newFilterLibrary[filterLibrary.length] = filter;
        filterLibrary = newFilterLibrary;

        activeFilters = new InputFilter[filterLibrary.length];

    
public voidclearFilters()
Clear filters.


        filterLibrary = new InputFilter[0];
        lastActiveFilter = -1;

    
public intdoRead(org.apache.tomcat.util.buf.ByteChunk chunk, org.apache.coyote.Request req)
Read some bytes.


        if (lastActiveFilter == -1)
            return inputStreamInputBuffer.doRead(chunk, req);
        else
            return activeFilters[lastActiveFilter].doRead(chunk,req);

    
public voidendRequest()
End request (consumes leftover bytes).

throws
IOException an undelying I/O error occured


        if (swallowInput && (lastActiveFilter != -1)) {
            int extraBytes = (int) activeFilters[lastActiveFilter].end();
            pos = pos - extraBytes;
        }

    
protected booleanfill()
Fill the internal buffer using data from the undelying input stream.

return
false if at end of stream


        int nRead = 0;

        if (parsingHeader) {

            if (lastValid == buf.length) {
                throw new IOException
                    (sm.getString("iib.requestheadertoolarge.error"));
            }

            nRead = inputStream.read(buf, pos, buf.length - lastValid);
            if (nRead > 0) {
                lastValid = pos + nRead;
            }

        } else {

            if (buf.length - end < 4500) {
                // In this case, the request header was really large, so we allocate a 
                // brand new one; the old one will get GCed when subsequent requests
                // clear all references
                buf = new byte[buf.length];
                end = 0;
            }
            pos = end;
            lastValid = pos;
            nRead = inputStream.read(buf, pos, buf.length - lastValid);
            if (nRead > 0) {
                lastValid = pos + nRead;
            }

        }
        return (nRead > 0);

    
public InputFilter[]getFilters()
Get filters.


        return filterLibrary;

    
public java.io.InputStreamgetInputStream()
Get the underlying socket input stream.


        return inputStream;

    
public voidnextRequest()
End processing of current HTTP request. Note: All bytes of the current request should have been already consumed. This method only resets all the pointers so that we are ready to parse the next HTTP request.


        // Recycle Request object
        request.recycle();

        // Determine the header buffer used for next request
        byte[] newHeaderBuf = null;
        // Copy leftover bytes to the beginning of the buffer
        if (lastValid - pos > 0) {
            int npos = 0;
            int opos = pos;
            while (lastValid - opos > opos - npos) {
                System.arraycopy(buf, opos, buf, npos, opos - npos);
                npos += pos;
                opos += pos;
            }
            System.arraycopy(buf, opos, buf, npos, lastValid - opos);
        }

        // Recycle filters
        for (int i = 0; i <= lastActiveFilter; i++) {
            activeFilters[i].recycle();
        }

        // Reset pointers
        lastValid = lastValid - pos;
        pos = 0;
        lastActiveFilter = -1;
        parsingHeader = true;
        swallowInput = true;
    
public booleanparseHeader()
Parse an HTTP header.

return
false after reading a blank line (which indicates that the HTTP header parsing is done


        //
        // Check for blank line
        //

        byte chr = 0;             
        while (true) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos];

            if ((chr == Constants.CR) || (chr == Constants.LF)) {
                if (chr == Constants.LF) {
                    pos++;        
                    return false;
                }
            } else {
                break;
            }

            pos++;

        }
        
        // Mark the current buffer position
        int start = pos;

        //
        // Reading the header name
        // Header name is always US-ASCII
        //

        boolean colon = false;
        //MessageBytes headerValue = null;
   
        while (!colon) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.COLON) {
                colon = true;
                headerValue = headers.addValue(buf, start, pos - start);
            }
            chr = buf[pos];
            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
            }

            pos++;

        } 
        
        // Mark the current buffer position
        start = pos;
        int realPos = pos;

        //
        // Reading the header value (which can be spanned over multiple lines)
        //

        boolean eol = false;
        boolean validLine = true;

        while (validLine) {

            boolean space = true;  
            // Skipping spaces
            while (space) {

                // Read new bytes if needed
                if (pos >= lastValid) {
                    if (!fill())
                        throw new EOFException(sm.getString("iib.eof.error"));
                }

                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
                    pos++;
                } else {
                    space = false;
                }

            }
            
            int lastSignificantChar = realPos;                 
            // Reading bytes until the end of the line
            while (!eol) {

                // Read new bytes if needed
                if (pos >= lastValid) {
                    if (!fill())
                        throw new EOFException(sm.getString("iib.eof.error"));
                }

                if (buf[pos] == Constants.CR) {
                } else if (buf[pos] == Constants.LF) {
                    eol = true;
                } else if (buf[pos] == Constants.SP) {
                    buf[realPos] = buf[pos];
                    realPos++;
                } else {
                    buf[realPos] = buf[pos];
                    realPos++;
                    lastSignificantChar = realPos;
                }

                pos++;

            }

            realPos = lastSignificantChar;        

            // Checking the first character of the new line. If the character
            // is a LWS, then it's a multiline header
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos];
            if ((chr != Constants.SP) && (chr != Constants.HT)) {
                validLine = false;
            } else {
                eol = false;
                // Copying one extra space in the buffer (since there must
                // be at least one space inserted between the lines)
                buf[realPos] = chr;
                realPos++;
            }
        }

        // Set the header value
        headerValue.setBytes(buf, start, realPos - start);

        return true;

    
public voidparseHeaders()
Parse the HTTP headers.


        while (parseHeader()) {
        }

        parsingHeader = false;
        end = pos;
    
public voidparseRequestLine()
Read the request line. This function is meant to be used during the HTTP request header parsing. Do NOT attempt to read the request body using it.

throws
IOException If an exception occurs during the underlying socket read operations, or if the given buffer is not big enough to accomodate the whole line.

        
        int start = 0;
        // END OF SJSAS 6231069
            
        byte chr = 0;
        do {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            chr = buf[pos++];

        } while ((chr == Constants.CR) || (chr == Constants.LF));

        pos--;

        // Mark the current buffer position
        start = pos;

        //
        // Reading the method name
        // Method name is always US-ASCII
        //

        boolean space = false;

        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.SP) {
                space = true;
                request.method().setBytes(buf, start, pos - start);
            }

            pos++;
        }

       
        // Mark the current buffer position
        start = pos;
        int end = 0;
        int questionPos = -1;

        //
        // Reading the URI
        //

        space = false;
        boolean eol = false;
        while (!space) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.SP) {
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.CR) 
                       || (buf[pos] == Constants.LF)) {
                // HTTP/0.9 style request
                eol = true;
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.QUESTION) 
                       && (questionPos == -1)) {
                questionPos = pos;
            }

            pos++;

        }
        
        request.unparsedURI().setBytes(buf, start, end - start);
        if (questionPos >= 0) {
            request.queryString().setBytes(buf, questionPos + 1, 
                                           end - questionPos - 1);
            request.requestURI().setBytes(buf, start, questionPos - start);
        } else {
            request.requestURI().setBytes(buf, start, end - start);
        }

        // Mark the current buffer position
        start = pos;
        end = 0;

        //
        // Reading the protocol
        // Protocol is always US-ASCII
        //   
        while (!eol) {

            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }

            if (buf[pos] == Constants.CR) {
                end = pos;
            } else if (buf[pos] == Constants.LF) {
                if (end == 0)
                    end = pos;
                eol = true;
            }

            pos++;

        }
           
        if ((end - start) > 0) {
            request.protocol().setBytes(buf, start, end - start);
        } else {
            request.protocol().setString("");
        }
    
public voidrecycle()
Recycle the input buffer. This should be called when closing the connection.


        // Recycle Request object
        request.recycle();

        inputStream = null;
        lastValid = 0;
        pos = 0;
        lastActiveFilter = -1;
        parsingHeader = true;
        swallowInput = true;
    
public voidsetInputStream(java.io.InputStream inputStream)
Set the underlying socket input stream.

    
    // END OF SJSAS 6231069

    
    // ------------------------------------------------------------- Properties


               
        

        // FIXME: Check for null ?

        this.inputStream = inputStream;

    
public voidsetSwallowInput(boolean swallowInput)
Set the swallow input flag.

        this.swallowInput = swallowInput;