All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mortbay.jetty.AbstractGenerator Maven / Gradle / Ivy

There is a newer version: 7.0.0.pre5
Show newest version
//========================================================================
//$Id: HttpGenerator.java,v 1.7 2005/11/25 21:17:12 gregwilkins Exp $
//Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed 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.mortbay.jetty;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.io.Buffer;
import org.mortbay.io.Buffers;
import org.mortbay.io.ByteArrayBuffer;
import org.mortbay.io.EndPoint;
import org.mortbay.io.View;
import org.mortbay.log.Log;
import org.mortbay.util.ByteArrayOutputStream2;
import org.mortbay.util.StringUtil;
import org.mortbay.util.TypeUtil;

/* ------------------------------------------------------------ */
/**
 * Abstract Generator. Builds HTTP Messages.
 * 
 * Currently this class uses a system parameter "jetty.direct.writers" to control
 * two optional writer to byte conversions. buffer.writers=true will probably be 
 * faster, but will consume more memory.   This option is just for testing and tuning.
 * 
 * @author gregw
 * 
 */
public abstract class AbstractGenerator implements Generator
{
    // states
    public final static int STATE_HEADER = 0;
    public final static int STATE_CONTENT = 2;
    public final static int STATE_FLUSHING = 3;
    public final static int STATE_END = 4;
    
    private static final byte[] NO_BYTES = {};
    private static int MAX_OUTPUT_CHARS = 512; 

    private static Buffer[] __reasons = new Buffer[505];
    static
    {
        Field[] fields = HttpServletResponse.class.getDeclaredFields();
        for (int i=0;i=STATE_FLUSHING)
            throw new IllegalStateException("Flushed");
        
        _last = false;
        _close = false;
        _contentWritten = 0;
        _contentLength = HttpTokens.UNKNOWN_CONTENT;
        _content=null;
        if (_buffer!=null)
            _buffer.clear();  
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the contentBufferSize.
     */
    public int getContentBufferSize()
    {
        return _contentBufferSize;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param contentBufferSize The contentBufferSize to set.
     */
    public void increaseContentBufferSize(int contentBufferSize)
    {
        if (contentBufferSize > _contentBufferSize)
        {
            _contentBufferSize = contentBufferSize;
            if (_buffer != null)
            {
                Buffer nb = _buffers.getBuffer(_contentBufferSize);
                nb.put(_buffer);
                _buffers.returnBuffer(_buffer);
                _buffer = nb;
            }
        }
    }
    
    /* ------------------------------------------------------------ */    
    public Buffer getUncheckedBuffer()
    {
        return _buffer;
    }
    
    /* ------------------------------------------------------------ */    
    public boolean getSendServerVersion ()
    {
        return _sendServerVersion;
    }
    
    /* ------------------------------------------------------------ */    
    public void setSendServerVersion (boolean sendServerVersion)
    {
        _sendServerVersion = sendServerVersion;
    }
    
    /* ------------------------------------------------------------ */
    public int getState()
    {
        return _state;
    }

    /* ------------------------------------------------------------ */
    public boolean isState(int state)
    {
        return _state == state;
    }

    /* ------------------------------------------------------------ */
    public boolean isComplete()
    {
        return _state == STATE_END;
    }

    /* ------------------------------------------------------------ */
    public boolean isIdle()
    {
        return _state == STATE_HEADER && _method==null && _status==0;
    }

    /* ------------------------------------------------------------ */
    public boolean isCommitted()
    {
        return _state != STATE_HEADER;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the head.
     */
    public boolean isHead()
    {
        return _head;
    }

    /* ------------------------------------------------------------ */
    public void setContentLength(long value)
    {
        if (value<0)
            _contentLength=HttpTokens.UNKNOWN_CONTENT;
        else
            _contentLength=value;
    }
    
    /* ------------------------------------------------------------ */
    /**
     * @param head The head to set.
     */
    public void setHead(boolean head)
    {
        _head = head;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return false if the connection should be closed after a request has been read,
     * true if it should be used for additional requests.
     */
    public boolean isPersistent()
    {
        return !_close;
    }

    /* ------------------------------------------------------------ */
    public void setPersistent(boolean persistent)
    {
        _close=!persistent;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param version The version of the client the response is being sent to (NB. Not the version
     *            in the response, which is the version of the server).
     */
    public void setVersion(int version)
    {
        if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");
        _version = version;
        if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null)
            _noContent=true;
    }

    /* ------------------------------------------------------------ */
    public int getVersion()
    {
        return _version;
    }
    
    /* ------------------------------------------------------------ */
    /**
     */
    public void setRequest(String method, String uri)
    {
        if (method==null || HttpMethods.GET.equals(method) )
            _method=HttpMethods.GET_BUFFER;
        else
            _method=HttpMethods.CACHE.lookup(method);
        _uri=uri;
        if (_version==HttpVersions.HTTP_0_9_ORDINAL)
            _noContent=true;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param status The status code to send.
     * @param reason the status message to send.
     */
    public void setResponse(int status, String reason)
    {
        if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");

        _status = status;
        if (reason!=null)
        {
            int len=reason.length();
            if (len>_headerBufferSize/2)
                len=_headerBufferSize/2;
            _reason=new ByteArrayBuffer(len);
            for (int i=0;i0;
    }
    
    /* ------------------------------------------------------------ */
    public boolean isContentWritten()
    {
        return _contentLength>=0 && _contentWritten>=_contentLength;
    }
    
    /* ------------------------------------------------------------ */
    public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException;
    
    /* ------------------------------------------------------------ */
    /**
     * Complete the message.
     * 
     * @throws IOException
     */
    public void complete() throws IOException
    {
        if (_state == STATE_HEADER)
        {
            throw new IllegalStateException("State==HEADER");
        }

        if (_contentLength >= 0 && _contentLength != _contentWritten && !_head)
        {
            if (Log.isDebugEnabled())
                Log.debug("ContentLength written=="+_contentWritten+" != contentLength=="+_contentLength);
            _close = true;
        }
    }

    /* ------------------------------------------------------------ */
    public abstract long flush() throws IOException;
    

    /* ------------------------------------------------------------ */
    /**
     * Utility method to send an error response. If the builder is not committed, this call is
     * equivalent to a setResponse, addcontent and complete call.
     * 
     * @param code
     * @param reason
     * @param content
     * @param close
     * @throws IOException
     */
    public void sendError(int code, String reason, String content, boolean close) throws IOException
    {
        if (close)
            _close = close;
        if (!isCommitted())
        {
            setResponse(code, reason);
            completeHeader(null, false);
            if (content != null) 
                addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);
            complete();
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the contentWritten.
     */
    public long getContentWritten()
    {
        return _contentWritten;
    }


    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /** Output.
     * 
     * 

* Implements {@link javax.servlet.ServletOutputStream} from the {@link javax.servlet} package. *

* A {@link ServletOutputStream} implementation that writes content * to a {@link AbstractGenerator}. The class is designed to be reused * and can be reopened after a close. */ public static class Output extends ServletOutputStream { protected AbstractGenerator _generator; protected long _maxIdleTime; protected ByteArrayBuffer _buf = new ByteArrayBuffer(NO_BYTES); protected boolean _closed; // These are held here for reuse by Writer String _characterEncoding; Writer _converter; char[] _chars; ByteArrayOutputStream2 _bytes; /* ------------------------------------------------------------ */ public Output(AbstractGenerator generator, long maxIdleTime) { _generator=generator; _maxIdleTime=maxIdleTime; } /* ------------------------------------------------------------ */ /* * @see java.io.OutputStream#close() */ public void close() throws IOException { _closed=true; } /* ------------------------------------------------------------ */ void blockForOutput() throws IOException { if (_generator._endp.isBlocking()) { try { flush(); } catch(IOException e) { _generator._endp.close(); throw e; } } else { if (!_generator._endp.blockWritable(_maxIdleTime)) { _generator._endp.close(); throw new EofException("timeout"); } _generator.flush(); } } /* ------------------------------------------------------------ */ void reopen() { _closed=false; } /* ------------------------------------------------------------ */ public void flush() throws IOException { // block until everything is flushed Buffer content = _generator._content; Buffer buffer = _generator._buffer; if (content!=null && content.length()>0 || buffer!=null && buffer.length()>0 || _generator.isBufferFull()) { _generator.flush(); while ((content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _generator._endp.isOpen()) blockForOutput(); } } /* ------------------------------------------------------------ */ public void write(byte[] b, int off, int len) throws IOException { _buf.wrap(b, off, len); write(_buf); _buf.wrap(NO_BYTES); } /* ------------------------------------------------------------ */ /* * @see java.io.OutputStream#write(byte[]) */ public void write(byte[] b) throws IOException { _buf.wrap(b); write(_buf); _buf.wrap(NO_BYTES); } /* ------------------------------------------------------------ */ /* * @see java.io.OutputStream#write(int) */ public void write(int b) throws IOException { if (_closed) throw new IOException("Closed"); if (!_generator._endp.isOpen()) throw new EofException(); // Block until we can add _content. while (_generator.isBufferFull()) { blockForOutput(); if (_closed) throw new IOException("Closed"); if (!_generator._endp.isOpen()) throw new EofException(); } // Add the _content if (_generator.addContent((byte)b)) // Buffers are full so flush. flush(); if (_generator.isContentWritten()) { flush(); close(); } } /* ------------------------------------------------------------ */ private void write(Buffer buffer) throws IOException { if (_closed) throw new IOException("Closed"); if (!_generator._endp.isOpen()) throw new EofException(); // Block until we can add _content. while (_generator.isBufferFull()) { blockForOutput(); if (_closed) throw new IOException("Closed"); if (!_generator._endp.isOpen()) throw new EofException(); } // Add the _content _generator.addContent(buffer, Generator.MORE); // Have to flush and complete headers? if (_generator.isBufferFull()) flush(); if (_generator.isContentWritten()) { flush(); close(); } // Block until our buffer is free while (buffer.length() > 0 && _generator._endp.isOpen()) blockForOutput(); } /* ------------------------------------------------------------ */ /* * @see javax.servlet.ServletOutputStream#print(java.lang.String) */ public void print(String s) throws IOException { write(s.getBytes()); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /** OutputWriter. * A writer that can wrap a {@link Output} stream and provide * character encodings. * * The UTF-8 encoding is done by this class and no additional * buffers or Writers are used. * The UTF-8 code was inspired by http://javolution.org */ public static class OutputWriter extends Writer { private static final int WRITE_CONV = 0; private static final int WRITE_ISO1 = 1; private static final int WRITE_UTF8 = 2; Output _out; AbstractGenerator _generator; int _writeMode; int _surrogate; /* ------------------------------------------------------------ */ public OutputWriter(Output out) { _out=out; _generator=_out._generator; } /* ------------------------------------------------------------ */ public void setCharacterEncoding(String encoding) { if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding)) { _writeMode = WRITE_ISO1; } else if (StringUtil.__UTF8.equalsIgnoreCase(encoding)) { _writeMode = WRITE_UTF8; } else { _writeMode = WRITE_CONV; if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding)) _out._converter = null; // Set lazily in getConverter() } _out._characterEncoding = encoding; if (_out._bytes==null) _out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS); } /* ------------------------------------------------------------ */ public void close() throws IOException { _out.close(); } /* ------------------------------------------------------------ */ public void flush() throws IOException { _out.flush(); } /* ------------------------------------------------------------ */ public void write (String s,int offset, int length) throws IOException { while (length > MAX_OUTPUT_CHARS) { write(s, offset, MAX_OUTPUT_CHARS); offset += MAX_OUTPUT_CHARS; length -= MAX_OUTPUT_CHARS; } if (_out._chars==null) { _out._chars = new char[MAX_OUTPUT_CHARS]; } char[] chars = _out._chars; s.getChars(offset, offset + length, chars, 0); write(chars, 0, length); } /* ------------------------------------------------------------ */ public void write (char[] s,int offset, int length) throws IOException { Output out = _out; while (length > 0) { out._bytes.reset(); int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length; switch (_writeMode) { case WRITE_CONV: { Writer converter=getConverter(); converter.write(s, offset, chars); converter.flush(); } break; case WRITE_ISO1: { byte[] buffer=out._bytes.getBuf(); int bytes=out._bytes.getCount(); if (chars>buffer.length-bytes) chars=buffer.length-bytes; for (int i = 0; i < chars; i++) { int c = s[offset+i]; buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255 } if (bytes>=0) out._bytes.setCount(bytes); break; } case WRITE_UTF8: { byte[] buffer=out._bytes.getBuf(); int bytes=out._bytes.getCount(); if (bytes+chars>buffer.length) chars=buffer.length-bytes; for (int i = 0; i < chars; i++) { int code = s[offset+i]; if ((code & 0xffffff80) == 0) { // 1b if (bytes+1>buffer.length) { chars=i; break; } buffer[bytes++]=(byte)(code); } else { if((code&0xfffff800)==0) { // 2b if (bytes+2>buffer.length) { chars=i; break; } buffer[bytes++]=(byte)(0xc0|(code>>6)); buffer[bytes++]=(byte)(0x80|(code&0x3f)); } else if((code&0xffff0000)==0) { // 3b if (bytes+3>buffer.length) { chars=i; break; } buffer[bytes++]=(byte)(0xe0|(code>>12)); buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f)); buffer[bytes++]=(byte)(0x80|(code&0x3f)); } else if((code&0xff200000)==0) { // 4b if (bytes+4>buffer.length) { chars=i; break; } buffer[bytes++]=(byte)(0xf0|(code>>18)); buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f)); buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f)); buffer[bytes++]=(byte)(0x80|(code&0x3f)); } else if((code&0xf4000000)==0) { // 5b if (bytes+5>buffer.length) { chars=i; break; } buffer[bytes++]=(byte)(0xf8|(code>>24)); buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f)); buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f)); buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f)); buffer[bytes++]=(byte)(0x80|(code&0x3f)); } else if((code&0x80000000)==0) { // 6b if (bytes+6>buffer.length) { chars=i; break; } buffer[bytes++]=(byte)(0xfc|(code>>30)); buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f)); buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f)); buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f)); buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f)); buffer[bytes++]=(byte)(0x80|(code&0x3f)); } else { buffer[bytes++]=(byte)('?'); } if (bytes==buffer.length) { chars=i+1; break; } } } out._bytes.setCount(bytes); break; } default: throw new IllegalStateException(); } out._bytes.writeTo(out); length-=chars; offset+=chars; } } /* ------------------------------------------------------------ */ private Writer getConverter() throws IOException { if (_out._converter == null) _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding); return _out._converter; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy