Mercurial Hosting > luan
view src/org/eclipse/jetty/http/AbstractGenerator.java @ 1015:0114d373748e
remove recycle()
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 24 Oct 2016 01:26:16 -0600 |
parents | 23ec25435b8c |
children | 8c13b9224cff |
line wrap: on
line source
// // ======================================================================== // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.http; import java.io.IOException; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.View; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* ------------------------------------------------------------ */ /** * 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. * */ abstract class AbstractGenerator { private static final Logger LOG = LoggerFactory.getLogger(AbstractGenerator.class); public static final boolean LAST=true; public static final boolean MORE=false; // states final static int STATE_HEADER = 0; final static int STATE_CONTENT = 2; final static int STATE_FLUSHING = 3; final static int STATE_END = 4; // data final Buffers _buffers; // source of buffers final EndPoint _endp; int _state = STATE_HEADER; int _status = 0; int _version = HttpVersions.HTTP_1_1_ORDINAL; Buffer _reason; Buffer _method; String _uri; long _contentWritten = 0; long _contentLength = HttpTokens.UNKNOWN_CONTENT; boolean _last = false; boolean _head = false; boolean _noContent = false; Boolean _persistent = null; Buffer _header; // Buffer for HTTP header (and maybe small _content) Buffer _buffer; // Buffer for copy of passed _content Buffer _content; // Buffer passed to addContent /* ------------------------------------------------------------------------------- */ /** * Constructor. * * @param buffers buffer pool * @param io the end point */ AbstractGenerator(Buffers buffers, EndPoint io) { this._buffers = buffers; this._endp = io; } /* ------------------------------------------------------------ */ /** * Add content. * * @param content * @param last * @throws IllegalArgumentException if <code>content</code> is {@link Buffer#isImmutable immutable}. * @throws IllegalStateException If the request is not expecting any more content, * or if the buffers are full and cannot be flushed. * @throws IOException if there is a problem flushing the buffers. */ public abstract void addContent(Buffer content, boolean last) throws IOException; abstract boolean isRequest(); public final boolean isOpen() { return _endp.isOpen(); } public void reset() { _state = STATE_HEADER; _status = 0; _version = HttpVersions.HTTP_1_1_ORDINAL; _reason = null; _last = false; _head = false; _noContent=false; _persistent = null; _contentWritten = 0; _contentLength = HttpTokens.UNKNOWN_CONTENT; _content = null; _method = null; } public final void returnBuffers() { if (_buffer!=null && _buffer.length()==0) { _buffers.returnBuffer(_buffer); _buffer=null; } if (_header!=null && _header.length()==0) { _buffers.returnBuffer(_header); _header=null; } } public final void resetBuffer() { if(_state>=STATE_FLUSHING) throw new IllegalStateException("Flushed"); _last = false; _persistent=null; _contentWritten = 0; _contentLength = HttpTokens.UNKNOWN_CONTENT; _content=null; if (_buffer!=null) _buffer.clear(); } /* ------------------------------------------------------------ */ /** * @return Returns the contentBufferSize. */ public final int getContentBufferSize() { if (_buffer==null) _buffer=_buffers.getBuffer(); return _buffer.capacity(); } public final Buffer getUncheckedBuffer() { return _buffer; } public final boolean isComplete() { return _state == STATE_END; } public final boolean isIdle() { return _state == STATE_HEADER && _method==null && _status==0; } public final boolean isCommitted() { return _state != STATE_HEADER; } public final void setContentLength(long value) { if (value<0) _contentLength=HttpTokens.UNKNOWN_CONTENT; else _contentLength=value; } public final void setHead(boolean head) { _head = head; } /* ------------------------------------------------------------ */ /** * @return <code>false</code> if the connection should be closed after a request has been read, * <code>true</code> if it should be used for additional requests. */ public final boolean isPersistent() { return _persistent!=null ?_persistent.booleanValue() :(isRequest()?true:_version>HttpVersions.HTTP_1_0_ORDINAL); } public final void setPersistent(boolean persistent) { _persistent = 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 final void setVersion(int version) { if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START "+_state); _version = version; if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null) _noContent=true; } /* ------------------------------------------------------------ */ /** * @param status The status code to send. * @param reason the status message to send. */ public final void setResponse(int status, String reason) { if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START"); _method=null; _status = status; if (reason!=null) { int len=reason.length(); // TODO don't hard code if (len>1024) len=1024; _reason=new ByteArrayBuffer(len); for (int i=0;i<len;i++) { char ch = reason.charAt(i); if (ch!='\r'&&ch!='\n') _reason.put((byte)ch); else _reason.put((byte)' '); } } } public final void completeUncheckedAddContent() { if (_noContent) { if(_buffer!=null) _buffer.clear(); } else { _contentWritten+=_buffer.length(); if (_head) _buffer.clear(); } } public boolean isBufferFull() { if (_buffer != null && _buffer.space()==0) { if (_buffer.length()==0 && !_buffer.isImmutable()) _buffer.compact(); return _buffer.space()==0; } return _content!=null && _content.length()>0; } public final boolean isWritten() { return _contentWritten>0; } public final boolean isAllContentWritten() { 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); _persistent = false; } } public abstract int flushBuffer() throws IOException; public final void flush(long maxIdleTime) throws IOException { // block until everything is flushed long now=System.currentTimeMillis(); long end=now+maxIdleTime; Buffer content = _content; Buffer buffer = _buffer; if (content!=null && content.length()>0 || buffer!=null && buffer.length()>0 || isBufferFull()) { flushBuffer(); while (now<end && (content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _endp.isOpen()&& !_endp.isOutputShutdown()) { blockForOutput(end-now); now=System.currentTimeMillis(); } } } /* ------------------------------------------------------------ */ /** * 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 The error code * @param reason The error reason * @param content Contents of the error page * @param close True if the connection should be closed * @throws IOException if there is a problem flushing the response */ public final void sendError(int code, String reason, String content, boolean close) throws IOException { if (close) _persistent=false; if (isCommitted()) { LOG.debug("sendError on committed: {} {}",code,reason); } else { LOG.debug("sendError: {} {}",code,reason); setResponse(code, reason); if (content != null) { completeHeader(null, false); addContent(new View(new ByteArrayBuffer(content)), LAST); } else if (code>=400) { completeHeader(null, false); addContent(new View(new ByteArrayBuffer("Error: "+(reason==null?(""+code):reason))), LAST); } else { completeHeader(null, true); } complete(); } } public final long getContentWritten() { return _contentWritten; } public final void blockForOutput(long maxIdleTime) throws IOException { if (_endp.isBlocking()) { try { flushBuffer(); } catch(IOException e) { _endp.close(); throw e; } } else { if (!_endp.blockWritable(maxIdleTime)) { _endp.close(); throw new EofException("timeout"); } flushBuffer(); } } }