FileDocCategorySizeDatePackage
InternalOutputBuffer.javaAPI DocGlassfish v2 API20665Fri Jun 15 09:37:52 BST 2007org.apache.coyote.http11

InternalOutputBuffer

public class InternalOutputBuffer extends Object implements org.apache.coyote.OutputBuffer, ByteChunk.ByteOutputChannel
Output buffer.
author
Remy Maucherat

Fields Summary
protected static final com.sun.org.apache.commons.logging.Log
log
Logger.
protected static final 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 read buffer.
protected int
pos
Position in the buffer.
protected OutputStream
outputStream
Underlying output stream.
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 org.apache.tomcat.util.buf.ByteChunk
socketBuffer
Socket buffer.
protected boolean
useSocketBuffer
Socket buffer (extra buffering to reduce number of packets sent).
Constructors Summary
public InternalOutputBuffer(org.apache.coyote.Response response)
Default constructor.

        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
    
public InternalOutputBuffer(org.apache.coyote.Response response, int headerBufferSize, boolean useSocketBuffer)
Create a new InternalOutputBuffer and configure the enable/disable the socketBuffer mechanism.

        
        this(response,headerBufferSize);
        this.useSocketBuffer = useSocketBuffer;
        if ( useSocketBuffer ){
            socketBuffer.allocate(headerBufferSize, headerBufferSize);
        }
    
public InternalOutputBuffer(org.apache.coyote.Response response, int headerBufferSize)
Alternate constructor.


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


        buf = new byte[headerBufferSize];

        outputStreamOutputBuffer = new OutputStreamOutputBuffer();

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

        socketBuffer = new ByteChunk();
        socketBuffer.setByteOutputChannel(this);

        committed = false;
        finished = false;

    
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];

    
public voidclearFilters()
Clear filters.


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

    
public voidcommit()
Commit the response.

throws
IOException an undelying I/O error occured


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

        /* GlassFish Issue 646
        if (pos > 0) {
            // Sending the response header buffer
            if (useSocketBuffer) {
                socketBuffer.append(buf, 0, pos);
            } else {
                outputStream.write(buf, 0, pos);
            }
            flush(false);
        }*/
        // START GlassFish Issue 646
        if (pos > 0) {
            flush(false);
        }
        // END GlassFish Issue 646

    
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.


        write(Constants.CRLF_BYTES);

    
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();

        if (useSocketBuffer) {
            socketBuffer.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
        if (useSocketBuffer) {
            socketBuffer.flushBuffer();
        }

    
private voidflush(boolean isFull)
Flush the buffer.

         // Sending the response header buffer
        realWriteBytes(buf, 0, pos);
        
        if ( isFull ) {
            pos = 0;
        }
    
public OutputFilter[]getFilters()
Get filters.


        return filterLibrary;

    
private java.lang.StringgetMessage(int message)

        if (System.getSecurityManager() != null){
           return (String)AccessController.doPrivileged(
                new PrivilegedAction(){
                    public Object run(){
                        return HttpMessages.getMessage(message); 
                    }
                }
           );
        } else {
            return HttpMessages.getMessage(message);
        }
    
public java.io.OutputStreamgetOutputStream()
Get the underlying socket output stream.


        return outputStream;

    
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();
        socketBuffer.recycle();

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

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

    
public voidrealWriteBytes(byte[] cbuf, int off, int len)
Callback to write data from the buffer.

        if (len > 0) {
            if (useSocketBuffer) {
                socketBuffer.append(cbuf, off, len);
            } else {
                outputStream.write(cbuf, off, len);
            }
        }
    
public voidrecycle()
Recycle the output buffer. This should be called when closing the connection.


        // Recycle Request object
        response.recycle();
        socketBuffer.recycle();

        outputStream = 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){
            realWriteBytes(Constants.ACK_BYTES,0,
                        Constants.ACK_BYTES.length);
        }
    
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);
        write(": ");
        write(value);
        write(Constants.CRLF_BYTES);

    
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);
        write(": ");
        write(value);
        write(Constants.CRLF_BYTES);

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

param
name Header name
param
value Header value


        write(name);
        write(": ");
        write(value);
        write(Constants.CRLF_BYTES);

    
public voidsendStatus()
Send the response status line.


        // Write protocol name
        write("HTTP/1.1 ");

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

        write(" ");

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

        // End the response status line
        if ( System.getSecurityManager() != null ){
           AccessController.doPrivileged(
                new PrivilegedAction(){
                    public Object run(){
                        write(Constants.CRLF_BYTES);
                        return null;
                    }
                }
           );
        } else {
            write(Constants.CRLF_BYTES);
        }

    
public voidsetOutputStream(java.io.OutputStream outputStream)
Set the underlying socket output stream.



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


               
        

        // FIXME: Check for null ?

        this.outputStream = outputStream;

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


        if (socketBufferSize > 500) {
            useSocketBuffer = true;
            socketBuffer.allocate(socketBufferSize, socketBufferSize);
        } else {
            useSocketBuffer = false;
        }

    
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

        try{
            realWriteBytes(bc.getBytes(),bc.getStart(),bc.getLength());
        } catch (IOException ex){
            ;
        }
    
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
bc 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];
            if (c != 9) {
                if ((c >= 0) && (c <= 31)) {
                    c = ' ";
                }
                if (c == 127) {
                    c = ' ";
                }
            }
            // issue #3157, if buffer is full - flush it
            if (pos >= buf.length) {
                try {
                    flush(true);
                } catch(IOException e) {
                    ;
                }
            }
            
            buf[pos++] = (byte) c;
        }
        
        try{
            flush(true);
        } catch (IOException ex){
            ;
        }    
    
protected 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

        try{
            realWriteBytes(b, 0, b.length);
        } catch (IOException ex){
            ;
        }
    
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);
            if (c != 9) {
                if ((c >= 0) && (c <= 31)) {
                    c = ' ";
                }
                if (c == 127) {
                    c = ' ";
                }
            }
            // issue #3157, if buffer is full - flush it
            if (pos >= buf.length) {
                try {
                    flush(true);
                } catch(IOException e) {
                    ;
                }
            }

            buf[pos++] = (byte) c;
        }
        
        try{
            flush(true);
        } catch (IOException ex){
            ;
        }    
    
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));