FileDocCategorySizeDatePackage
InternalNioOutputBuffer.javaAPI DocApache Tomcat 6.0.1420962Fri Jul 20 04:20:36 BST 2007org.apache.coyote.http11

InternalNioOutputBuffer

public class InternalNioOutputBuffer extends Object implements org.apache.coyote.OutputBuffer
Output buffer.
author
Remy Maucherat
author
Filip Hanik

Fields Summary
int
bbufLimit
protected static org.apache.tomcat.util.res.StringManager
sm
The string manager for this package.
protected org.apache.coyote.Response
response
Associated Coyote response.
protected org.apache.tomcat.util.http.MimeHeaders
headers
Headers of the associated request.
protected boolean
committed
Committed flag.
protected boolean
finished
Finished flag.
protected byte[]
buf
Pointer to the current write buffer.
protected int
pos
Position in the buffer.
protected org.apache.tomcat.util.net.NioChannel
socket
Underlying socket.
protected org.apache.tomcat.util.net.NioSelectorPool
pool
Selector pool, for blocking reads and blocking writes
protected org.apache.coyote.OutputBuffer
outputStreamOutputBuffer
Underlying output buffer.
protected OutputFilter[]
filterLibrary
Filter library. Note: Filter[0] is always the "chunked" filter.
protected OutputFilter[]
activeFilters
Active filter (which is actually the top of the pipeline).
protected int
lastActiveFilter
Index of the last active filter.
protected long
writeTimeout
Write time out in milliseconds
int
total
Constructors Summary
public InternalNioOutputBuffer(org.apache.coyote.Response response)
Default constructor.

    
    
           
       
        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE, 10000);
    
public InternalNioOutputBuffer(org.apache.coyote.Response response, int headerBufferSize, long writeTimeout)
Alternate constructor.


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

        buf = new byte[headerBufferSize];
        
        if (headerBufferSize < (8 * 1024)) {
            bbufLimit = 6 * 1500;    
        } else {
            bbufLimit = (headerBufferSize / 1500 + 1) * 1500;
        }
        //bbuf = ByteBuffer.allocateDirect(bbufLimit);

        outputStreamOutputBuffer = new SocketOutputBuffer();

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

        committed = false;
        finished = false;
        
        this.writeTimeout = writeTimeout;

        // Cause loading of HttpMessages
        HttpMessages.getMessage(200);

    
Methods Summary
public voidaddActiveFilter(OutputFilter filter)
Add an output filter to the filter library.


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

        activeFilters[++lastActiveFilter] = filter;

        filter.setResponse(response);

    
public voidaddFilter(OutputFilter filter)
Add an output filter to the filter library.


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

        activeFilters = new OutputFilter[filterLibrary.length];

    
private synchronized voidaddToBB(byte[] buf, int offset, int length)

               
        while (socket.getBufHandler().getWriteBuffer().remaining() < length) {
            flushBuffer();
        }
        socket.getBufHandler().getWriteBuffer().put(buf, offset, length);
        total += length;
        NioEndpoint.KeyAttachment ka = (NioEndpoint.KeyAttachment)socket.getAttachment(false);
        if ( ka!= null ) ka.access();//prevent timeouts for just doing client writes
    
public voidclearFilters()
Clear filters.


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

    
protected voidcommit()
Commit the response.

throws
IOException an undelying I/O error occured


        // The response is now committed
        committed = true;
        response.setCommitted(true);

        if (pos > 0) {
            // Sending the response header buffer
            addToBB(buf, 0, pos);
        }

    
public intdoWrite(org.apache.tomcat.util.buf.ByteChunk chunk, org.apache.coyote.Response res)
Write the contents of a byte chunk.

param
chunk byte chunk
return
number of bytes written
throws
IOException an undelying I/O error occured


        if (!committed) {

            // Send the connector a request for commit. The connector should
            // then validate the headers, send them (using sendHeaders) and 
            // set the filters accordingly.
            response.action(ActionCode.ACTION_COMMIT, null);

        }

        if (lastActiveFilter == -1)
            return outputStreamOutputBuffer.doWrite(chunk, res);
        else
            return activeFilters[lastActiveFilter].doWrite(chunk, res);

    
public voidendHeaders()
End the header block.


        buf[pos++] = Constants.CR;
        buf[pos++] = Constants.LF;

    
public voidendRequest()
End request.

throws
IOException an undelying I/O error occured


        if (!committed) {

            // Send the connector a request for commit. The connector should
            // then validate the headers, send them (using sendHeader) and 
            // set the filters accordingly.
            response.action(ActionCode.ACTION_COMMIT, null);

        }

        if (finished)
            return;

        if (lastActiveFilter != -1)
            activeFilters[lastActiveFilter].end();

        flushBuffer();

        finished = true;

    
public voidflush()
Flush the response.

throws
IOException an undelying I/O error occured


        if (!committed) {

            // Send the connector a request for commit. The connector should
            // then validate the headers, send them (using sendHeader) and 
            // set the filters accordingly.
            response.action(ActionCode.ACTION_COMMIT, null);

        }

        // Flush the current buffer
        flushBuffer();

    
protected voidflushBuffer()
Callback to write data from the buffer.


        //prevent timeout for async,
        SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
        if (key != null) {
            NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
            attach.access();
        }

        //write to the socket, if there is anything to write
        if (socket.getBufHandler().getWriteBuffer().position() > 0) {
            writeToSocket(socket.getBufHandler().getWriteBuffer(),true);
        }
    
public OutputFilter[]getFilters()
Get filters.


        return filterLibrary;

    
public org.apache.tomcat.util.net.NioSelectorPoolgetSelectorPool()

        return pool;
    
public org.apache.tomcat.util.net.NioChannelgetSocket()
Get the underlying socket input stream.

        return socket;
    
public longgetWriteTimeout()

        return writeTimeout;
    
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
        response.recycle();

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

        // Reset pointers
        pos = 0;
        lastActiveFilter = -1;
        committed = false;
        finished = false;

    
public voidrecycle()
Recycle the output buffer. This should be called when closing the connection.


        // Recycle Request object
        response.recycle();
        socket.getBufHandler().getWriteBuffer().clear();

        socket = null;
        pos = 0;
        lastActiveFilter = -1;
        committed = false;
        finished = false;

    
public voidreset()
Reset current response.

throws
IllegalStateException if the response has already been committed


        if (committed)
            throw new IllegalStateException(/*FIXME:Put an error message*/);

        // Recycle Request object
        response.recycle();

    
public voidsendAck()
Send an acknoledgement.


        if (!committed) {
            //Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0
            ByteBuffer buf = ByteBuffer.wrap(Constants.ACK_BYTES,0,Constants.ACK_BYTES.length);    
            writeToSocket(buf,false);
        }

    
public voidsendHeader(org.apache.tomcat.util.buf.MessageBytes name, org.apache.tomcat.util.buf.MessageBytes value)
Send a header.

param
name Header name
param
value Header value


        write(name);
        buf[pos++] = Constants.COLON;
        buf[pos++] = Constants.SP;
        write(value);
        buf[pos++] = Constants.CR;
        buf[pos++] = Constants.LF;

    
public voidsendHeader(org.apache.tomcat.util.buf.ByteChunk name, org.apache.tomcat.util.buf.ByteChunk value)
Send a header.

param
name Header name
param
value Header value


        write(name);
        buf[pos++] = Constants.COLON;
        buf[pos++] = Constants.SP;
        write(value);
        buf[pos++] = Constants.CR;
        buf[pos++] = Constants.LF;

    
public voidsendHeader(java.lang.String name, java.lang.String value)
Send a header.

param
name Header name
param
value Header value


        write(name);
        buf[pos++] = Constants.COLON;
        buf[pos++] = Constants.SP;
        write(value);
        buf[pos++] = Constants.CR;
        buf[pos++] = Constants.LF;

    
public voidsendStatus()
Send the response status line.


        // Write protocol name
        write(Constants.HTTP_11_BYTES);
        buf[pos++] = Constants.SP;

        // Write status code
        int status = response.getStatus();
        switch (status) {
        case 200:
            write(Constants._200_BYTES);
            break;
        case 400:
            write(Constants._400_BYTES);
            break;
        case 404:
            write(Constants._404_BYTES);
            break;
        default:
            write(status);
        }

        buf[pos++] = Constants.SP;

        // Write message
        String message = response.getMessage();
        if (message == null) {
            write(HttpMessages.getMessage(status));
        } else {
            write(message);
        }

        // End the response status line
        buf[pos++] = Constants.CR;
        buf[pos++] = Constants.LF;

    
public voidsetSelectorPool(org.apache.tomcat.util.net.NioSelectorPool pool)

 
        this.pool = pool;
    
public voidsetSocket(org.apache.tomcat.util.net.NioChannel socket)
Set the underlying socket.



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


             
        
        this.socket = socket;
    
public voidsetSocketBuffer(int socketBufferSize)
Set the socket buffer size.

        // FIXME: Remove
    
public voidsetWriteTimeout(long writeTimeout)

        this.writeTimeout = writeTimeout;
    
protected voidwrite(org.apache.tomcat.util.buf.MessageBytes mb)
This method will write the contents of the specyfied message bytes buffer to the output stream, without filtering. This method is meant to be used to write the response header.

param
mb data to be written


        if (mb.getType() == MessageBytes.T_BYTES) {
            ByteChunk bc = mb.getByteChunk();
            write(bc);
        } else if (mb.getType() == MessageBytes.T_CHARS) {
            CharChunk cc = mb.getCharChunk();
            write(cc);
        } else {
            write(mb.toString());
        }

    
protected voidwrite(org.apache.tomcat.util.buf.ByteChunk bc)
This method will write the contents of the specyfied message bytes buffer to the output stream, without filtering. This method is meant to be used to write the response header.

param
bc data to be written


        // Writing the byte chunk to the output buffer
        int length = bc.getLength();
        System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length);
        pos = pos + length;

    
protected voidwrite(org.apache.tomcat.util.buf.CharChunk cc)
This method will write the contents of the specyfied char buffer to the output stream, without filtering. This method is meant to be used to write the response header.

param
cc data to be written


        int start = cc.getStart();
        int end = cc.getEnd();
        char[] cbuf = cc.getBuffer();
        for (int i = start; i < end; i++) {
            char c = cbuf[i];
            // Note:  This is clearly incorrect for many strings,
            // but is the only consistent approach within the current
            // servlet framework.  It must suffice until servlet output
            // streams properly encode their output.
            if ((c <= 31) && (c != 9)) {
                c = ' ";
            } else if (c == 127) {
                c = ' ";
            }
            buf[pos++] = (byte) c;
        }

    
public voidwrite(byte[] b)
This method will write the contents of the specyfied byte buffer to the output stream, without filtering. This method is meant to be used to write the response header.

param
b data to be written


        // Writing the byte chunk to the output buffer
        System.arraycopy(b, 0, buf, pos, b.length);
        pos = pos + b.length;

    
protected voidwrite(java.lang.String s)
This method will write the contents of the specyfied String to the output stream, without filtering. This method is meant to be used to write the response header.

param
s data to be written


        if (s == null)
            return;

        // From the Tomcat 3.3 HTTP/1.0 connector
        int len = s.length();
        for (int i = 0; i < len; i++) {
            char c = s.charAt (i);
            // Note:  This is clearly incorrect for many strings,
            // but is the only consistent approach within the current
            // servlet framework.  It must suffice until servlet output
            // streams properly encode their output.
            if ((c <= 31) && (c != 9)) {
                c = ' ";
            } else if (c == 127) {
                c = ' ";
            }
            buf[pos++] = (byte) c;
        }

    
protected voidwrite(int i)
This method will print the specified integer to the output stream, without filtering. This method is meant to be used to write the response header.

param
i data to be written


        write(String.valueOf(i));

    
private synchronized voidwriteToSocket(java.nio.ByteBuffer bytebuffer, boolean flip)

        //int limit = bytebuffer.position();
        if ( flip ) bytebuffer.flip();
        int written = 0;
        Selector selector = null;
        try {
            selector = getSelectorPool().get();
        } catch ( IOException x ) {
            //ignore
        }
        try {
            written = getSelectorPool().write(bytebuffer, socket, selector, writeTimeout);
            //make sure we are flushed 
            do {
                if (socket.flush(true,selector,writeTimeout)) break;
            }while ( true );
        }finally { 
            if ( selector != null ) getSelectorPool().put(selector);
        }
        socket.getBufHandler().getWriteBuffer().clear();
        this.total = 0;