jetty-9 committed to taking the IO out of the generator as well now. This is currently a total work in progress... with a lot of work to do

This commit is contained in:
Greg Wilkins 2012-02-10 16:37:46 +11:00
parent 824509b0fb
commit 5c725d4e42
8 changed files with 491 additions and 1040 deletions

View File

@ -1,522 +0,0 @@
// ========================================================================
// Copyright (c) 2004-2009 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 java.nio.ByteBuffer;
import javax.swing.text.View;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* 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.
*
*/
public abstract class AbstractGenerator implements Generator
{
private static final Logger LOG = Log.getLogger(AbstractGenerator.class);
// 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;
public static final byte[] NO_BYTES = {};
// data
protected final Buffers _buffers; // source of buffers
protected final EndPoint _endp;
protected int _state = STATE_HEADER;
protected int _status = 0;
protected HttpVersion _version = HttpVersion.HTTP_1_1;
protected ByteBuffer _reason;
protected ByteBuffer _method;
protected String _uri;
protected long _contentWritten = 0;
protected long _contentLength = HttpTokens.UNKNOWN_CONTENT;
protected boolean _last = false;
protected boolean _head = false;
protected boolean _noContent = false;
protected Boolean _persistent = null;
protected ByteBuffer _header; // Buffer for HTTP header (and maybe small _content)
protected ByteBuffer _buffer; // Buffer for copy of passed _content
protected ByteBuffer _content; // Buffer passed to addContent
protected ByteBuffer _date;
private boolean _sendServerVersion;
/* ------------------------------------------------------------------------------- */
/**
* Constructor.
*
* @param buffers buffer pool
* @param io the end point
*/
public AbstractGenerator(Buffers buffers, EndPoint io)
{
this._buffers = buffers;
this._endp = io;
}
/* ------------------------------------------------------------------------------- */
public abstract boolean isRequest();
/* ------------------------------------------------------------------------------- */
public abstract boolean isResponse();
/* ------------------------------------------------------------------------------- */
public boolean isOpen()
{
return _endp.isOpen();
}
/* ------------------------------------------------------------------------------- */
public void reset()
{
_state = STATE_HEADER;
_status = 0;
_version = HttpVersion.HTTP_1_1;
_reason = null;
_last = false;
_head = false;
_noContent=false;
_persistent = null;
_contentWritten = 0;
_contentLength = HttpTokens.UNKNOWN_CONTENT;
_date = null;
_content = null;
_method=null;
}
/* ------------------------------------------------------------------------------- */
public 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 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 int getContentBufferSize()
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
return _buffer.capacity();
}
/* ------------------------------------------------------------ */
/**
* @param contentBufferSize The contentBufferSize to set.
*/
public void increaseContentBufferSize(int contentBufferSize)
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
if (contentBufferSize > _buffer.capacity())
{
ByteBuffer nb = _buffers.getBuffer(contentBufferSize);
nb.put(_buffer);
_buffers.returnBuffer(_buffer);
_buffer = nb;
}
}
/* ------------------------------------------------------------ */
public ByteBuffer 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 <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 boolean isPersistent()
{
return _persistent!=null
?_persistent.booleanValue()
:(isRequest()?true:_version>HttpVersion.HTTP_1_0_ORDINAL);
}
/* ------------------------------------------------------------ */
public 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 void setVersion(int version)
{
if (_state != STATE_HEADER)
throw new IllegalStateException("STATE!=START "+_state);
_version = version;
if (_version==HttpVersion.HTTP_0_9_ORDINAL && _method!=null)
_noContent=true;
}
/* ------------------------------------------------------------ */
public int getVersion()
{
return _version;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.Generator#setDate(org.eclipse.jetty.io.ByteBuffer)
*/
public void setDate(ByteBuffer timeStampBuffer)
{
_date=timeStampBuffer;
}
/* ------------------------------------------------------------ */
/**
*/
public void setRequest(String method, String uri)
{
if (method==null || HttpMethod.GET.equals(method) )
_method=HttpMethod.GET_BUFFER;
else
_method=HttpMethod.CACHE.lookup(method);
_uri=uri;
if (_version==HttpVersion.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");
_method=null;
_status = status;
if (reason!=null)
{
int len=reason.length();
// TODO don't hard code
if (len>1024)
len=1024;
_reason=BufferUtil.allocate(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)' ');
}
}
}
/* ------------------------------------------------------------ */
/** Prepare buffer for unchecked writes.
* Prepare the generator buffer to receive unchecked writes
* @return the available space in the buffer.
* @throws IOException
*/
public abstract int prepareUncheckedAddContent() throws IOException;
/* ------------------------------------------------------------ */
void uncheckedAddContent(int b)
{
_buffer.put((byte)b);
}
/* ------------------------------------------------------------ */
public 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 boolean isWritten()
{
return _contentWritten>0;
}
/* ------------------------------------------------------------ */
public 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 void flush(long maxIdleTime) throws IOException
{
// block until everything is flushed
long now=System.currentTimeMillis();
long end=now+maxIdleTime;
ByteBuffer content = _content;
ByteBuffer 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 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(BufferUtil.allocate(content)), Generator.LAST);
}
else
{
completeHeader(null, true);
}
complete();
}
}
/* ------------------------------------------------------------ */
/**
* @return Returns the contentWritten.
*/
public long getContentWritten()
{
return _contentWritten;
}
/* ------------------------------------------------------------ */
public 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();
}
}
}

View File

@ -1,101 +0,0 @@
// ========================================================================
// Copyright (c) 2006-2009 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 java.nio.ByteBuffer;
public interface Generator
{
public static final boolean LAST=true;
public static final boolean MORE=false;
/* ------------------------------------------------------------ */
/**
* Add content.
*
* @param content
* @param last
* @throws IllegalArgumentException if <code>content</code> is {@link ByteBuffer#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.
*/
void addContent(ByteBuffer content, boolean last) throws IOException;
/* ------------------------------------------------------------ */
/**
* Add content.
*
* @param b byte
* @return true if the buffers are full
* @throws IOException
*/
boolean addContent(byte b) throws IOException;
void complete() throws IOException;
void completeHeader(HttpFields responseFields, boolean last) throws IOException;
int flushBuffer() throws IOException;
int getContentBufferSize();
long getContentWritten();
boolean isWritten();
boolean isAllContentWritten();
void increaseContentBufferSize(int size);
boolean isBufferFull();
boolean isCommitted();
boolean isComplete();
boolean isPersistent();
void reset();
void resetBuffer();
void returnBuffers();
void sendError(int code, String reason, String content, boolean close) throws IOException;
void setHead(boolean head);
void setRequest(String method, String uri);
void setResponse(int status, String reason);
void setSendServerVersion(boolean sendServerVersion);
void setVersion(int version);
boolean isIdle();
void setContentLength(long length);
void setPersistent(boolean persistent);
void setDate(ByteBuffer timeStampBuffer);
}

View File

@ -1048,6 +1048,7 @@ public class HttpFields
_next = null;
}
/* ------------------------------------------------------------ */
private byte[] toSanitisedBytes(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
@ -1091,6 +1092,12 @@ public class HttpFields
BufferUtil.putCRLF(buffer);
}
/* ------------------------------------------------------------ */
public void putValueTo(ByteBuffer buffer) throws IOException
{
buffer.put(toSanitisedBytes(_value));
}
/* ------------------------------------------------------------ */
public String getName()

View File

@ -17,6 +17,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringMap;
import org.eclipse.jetty.util.StringUtil;
public enum HttpHeader
@ -101,7 +102,9 @@ public enum HttpHeader
SET_COOKIE("Set-Cookie"),
SET_COOKIE2("Set-Cookie2"),
MIME_VERSION("MIME-Version"),
IDENTITY("identity");
IDENTITY("identity"),
UNKNOWN("::UNKNOWN::");
/* ------------------------------------------------------------ */
@ -109,17 +112,22 @@ public enum HttpHeader
static
{
for (HttpHeader header : HttpHeader.values())
CACHE.put(header.toString(),header);
if (header!=UNKNOWN)
CACHE.put(header.toString(),header);
}
private final String _string;
private final byte[] _bytes;
private final byte[] _bytesColonSpace;
private final ByteBuffer _buffer;
/* ------------------------------------------------------------ */
HttpHeader(String s)
{
_string=s;
_buffer=BufferUtil.toBuffer(s);
_bytes=StringUtil.getBytes(s);
_bytesColonSpace=StringUtil.getBytes(s+": ");
_buffer=ByteBuffer.wrap(_bytes);
}
/* ------------------------------------------------------------ */
@ -128,6 +136,18 @@ public enum HttpHeader
return _buffer.asReadOnlyBuffer();
}
/* ------------------------------------------------------------ */
public byte[] toBytes()
{
return _bytes;
}
/* ------------------------------------------------------------ */
public byte[] toBytesColonSpace()
{
return _bytesColonSpace;
}
/* ------------------------------------------------------------ */
public String toString()
{

View File

@ -35,14 +35,16 @@ public enum HttpHeaderValue
TE("TE"),
BYTES("bytes"),
NO_CACHE("no-cache"),
UPGRADE("Upgrade");
UPGRADE("Upgrade"),
UNKNOWN("::UNKNOWN::");
/* ------------------------------------------------------------ */
public final static StringMap<HttpHeaderValue> CACHE= new StringMap<HttpHeaderValue>(true);
static
{
for (HttpHeaderValue value : HttpHeaderValue.values())
CACHE.put(value.toString(),value);
if (value!=UNKNOWN)
CACHE.put(value.toString(),value);
}
private final String _string;

View File

@ -17,6 +17,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringMap;
import org.eclipse.jetty.util.StringUtil;
/* ------------------------------------------------------------------------------- */
@ -35,6 +36,7 @@ public enum HttpVersion
}
private final String _string;
private final byte[] _bytes;
private final ByteBuffer _buffer;
private final int _version;
@ -42,10 +44,17 @@ public enum HttpVersion
HttpVersion(String s,int version)
{
_string=s;
_buffer=BufferUtil.toBuffer(s);
_bytes=StringUtil.getBytes(s);
_buffer=ByteBuffer.wrap(_bytes);
_version=version;
}
/* ------------------------------------------------------------ */
public byte[] toBytes()
{
return _bytes;
}
/* ------------------------------------------------------------ */
public ByteBuffer toBuffer()
{

View File

@ -352,14 +352,7 @@ public class StringUtil
/* ------------------------------------------------------------ */
public static String toUTF8String(byte[] b,int offset,int length)
{
try
{
return new String(b,offset,length,__UTF8);
}
catch (UnsupportedEncodingException e)
{
throw new IllegalArgumentException(e);
}
return new String(b,offset,length,__UTF8_CHARSET);
}
/* ------------------------------------------------------------ */
@ -418,15 +411,7 @@ public class StringUtil
public static byte[] getBytes(String s)
{
try
{
return s.getBytes(__ISO_8859_1);
}
catch(Exception e)
{
LOG.warn(e);
return s.getBytes();
}
return s.getBytes(__ISO_8859_1_CHARSET);
}
public static byte[] getBytes(String s,String charset)