applied jetty-7p 7f68abf..b2e4270, 7f68abf..a900c5e, 9a93c9e..9f94539. decomposed async and blocking handling. Simplified HttpFields and Buffer handling from the HttpParser.
This commit is contained in:
parent
a7eabf0757
commit
b890ff8fb2
|
@ -97,8 +97,8 @@ public class LikeJettyXml
|
|||
HandlerCollection handlers = new HandlerCollection();
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
handlers.setHandlers(new Handler[]
|
||||
{ contexts, new DefaultHandler(), requestLogHandler });
|
||||
|
||||
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler });
|
||||
|
||||
StatisticsHandler stats = new StatisticsHandler();
|
||||
stats.setHandler(handlers);
|
||||
|
@ -136,6 +136,7 @@ public class LikeJettyXml
|
|||
server.setStopAtShutdown(true);
|
||||
server.setSendServerVersion(true);
|
||||
|
||||
|
||||
server.start();
|
||||
|
||||
server.join();
|
||||
|
|
|
@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.eclipse.jetty.http.HttpException;
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.BlockingHttpConnection;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
|
@ -35,7 +36,7 @@ import org.eclipse.jetty.server.Server;
|
|||
* duplication of HttpConnection
|
||||
*
|
||||
*/
|
||||
public class Ajp13Connection extends HttpConnection
|
||||
public class Ajp13Connection extends BlockingHttpConnection
|
||||
{
|
||||
public Ajp13Connection(Connector connector, EndPoint endPoint, Server server)
|
||||
{
|
||||
|
|
|
@ -133,7 +133,7 @@ public class Ajp13Parser implements Parser
|
|||
public void parse() throws IOException
|
||||
{
|
||||
if (_state == STATE_END)
|
||||
reset(false);
|
||||
reset();
|
||||
if (_state != STATE_START)
|
||||
throw new IllegalStateException("!START");
|
||||
|
||||
|
@ -206,7 +206,7 @@ public class Ajp13Parser implements Parser
|
|||
{
|
||||
// This is normal in AJP since the socket closes on timeout only
|
||||
Log.debug(e);
|
||||
reset(true);
|
||||
reset();
|
||||
throw (e instanceof EofException) ? e : new EofException(e);
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ public class Ajp13Parser implements Parser
|
|||
_handler.messageComplete(_contentPosition);
|
||||
return filled;
|
||||
}
|
||||
reset(true);
|
||||
reset();
|
||||
throw new EofException();
|
||||
}
|
||||
|
||||
|
@ -317,7 +317,7 @@ public class Ajp13Parser implements Parser
|
|||
|
||||
_buffer= null;
|
||||
|
||||
reset(true);
|
||||
reset();
|
||||
|
||||
return -1;
|
||||
case Ajp13Packet.SHUTDOWN_ORDINAL:
|
||||
|
@ -606,7 +606,7 @@ public class Ajp13Parser implements Parser
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void reset(boolean returnBuffers)
|
||||
public void reset()
|
||||
{
|
||||
_state = STATE_START;
|
||||
_contentLength = HttpTokens.UNKNOWN_CONTENT;
|
||||
|
@ -614,51 +614,59 @@ public class Ajp13Parser implements Parser
|
|||
_length = 0;
|
||||
_packetLength = 0;
|
||||
|
||||
if (_body != null)
|
||||
if (_body!=null && _body.hasContent())
|
||||
{
|
||||
if (_body.hasContent())
|
||||
// There is content in the body after the end of the request.
|
||||
// This is probably a pipelined header of the next request, so we need to
|
||||
// copy it to the header buffer.
|
||||
if (_header==null)
|
||||
{
|
||||
_header=_buffers.getHeader();
|
||||
_tok0.update(_header);
|
||||
_tok0.update(0,0);
|
||||
_tok1.update(_header);
|
||||
_tok1.update(0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_header.setMarkIndex(-1);
|
||||
_header.compact();
|
||||
// TODO if pipelined requests received after big
|
||||
// input - maybe this is not good?.
|
||||
_body.skip(_header.put(_body));
|
||||
|
||||
}
|
||||
|
||||
if (_body.length() == 0)
|
||||
{
|
||||
if (_buffers != null && returnBuffers)
|
||||
_buffers.returnBuffer(_body);
|
||||
_body = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_body.setMarkIndex(-1);
|
||||
_body.compact();
|
||||
}
|
||||
int take=_header.space();
|
||||
if (take>_body.length())
|
||||
take=_body.length();
|
||||
_body.peek(_body.getIndex(),take);
|
||||
_body.skip(_header.put(_body.peek(_body.getIndex(),take)));
|
||||
}
|
||||
|
||||
if (_header != null)
|
||||
{
|
||||
if (_header!=null)
|
||||
_header.setMarkIndex(-1);
|
||||
if (!_header.hasContent() && _buffers != null && returnBuffers)
|
||||
{
|
||||
_buffers.returnBuffer(_header);
|
||||
_header = null;
|
||||
_buffer = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_header.compact();
|
||||
_tok0.update(_header);
|
||||
_tok0.update(0, 0);
|
||||
_tok1.update(_header);
|
||||
_tok1.update(0, 0);
|
||||
}
|
||||
if (_body!=null)
|
||||
_body.setMarkIndex(-1);
|
||||
|
||||
_buffer=_header;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void returnBuffers()
|
||||
{
|
||||
if (_body!=null && !_body.hasContent() && _body.markIndex()==-1)
|
||||
{
|
||||
if (_buffer==_body)
|
||||
_buffer=_header;
|
||||
if (_buffers!=null)
|
||||
_buffers.returnBuffer(_body);
|
||||
_body=null;
|
||||
}
|
||||
|
||||
_buffer = _header;
|
||||
if (_header!=null && !_header.hasContent() && _header.markIndex()==-1)
|
||||
{
|
||||
if (_buffer==_header)
|
||||
_buffer=null;
|
||||
_buffers.returnBuffer(_header);
|
||||
_header=null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
|
|
@ -535,7 +535,9 @@ public class HttpConnection extends AbstractConnection implements Dumpable
|
|||
{
|
||||
_requestComplete = false;
|
||||
_connectionHeader = null;
|
||||
_parser.reset(returnBuffers);
|
||||
_parser.reset();
|
||||
if (returnBuffers)
|
||||
_parser.returnBuffers();
|
||||
_generator.reset(returnBuffers);
|
||||
_http11 = true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.client;
|
|||
import java.io.FileInputStream;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ExternalKeyStoreAsyncSslHttpExchangeTest extends SslHttpExchangeTest
|
||||
{
|
||||
|
@ -36,4 +37,11 @@ public class ExternalKeyStoreAsyncSslHttpExchangeTest extends SslHttpExchangeTes
|
|||
_httpClient.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBigPostWithContentExchange() throws Exception
|
||||
{
|
||||
super.testBigPostWithContentExchange();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
@ -28,6 +29,8 @@ import java.util.Map;
|
|||
import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.BufferCache;
|
||||
|
@ -55,7 +58,7 @@ public class HttpFields
|
|||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
|
||||
public final static BufferDateCache __dateCache = new BufferDateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
|
||||
public static final BufferDateCache __dateCache = new BufferDateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
static
|
||||
|
@ -159,8 +162,6 @@ public class HttpFields
|
|||
StringUtil.append2digits(buf, seconds);
|
||||
buf.append(" GMT");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -201,8 +202,6 @@ public class HttpFields
|
|||
formatCookieDate(buf, date);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private final static String __dateReceiveFmt[] =
|
||||
|
@ -219,7 +218,8 @@ public class HttpFields
|
|||
"EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
|
||||
"EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
|
||||
};
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private static class DateParser
|
||||
{
|
||||
final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
|
||||
|
@ -289,9 +289,8 @@ public class HttpFields
|
|||
|
||||
/* -------------------------------------------------------------- */
|
||||
private final ArrayList<Field> _fields = new ArrayList<Field>(20);
|
||||
private final HashMap<Buffer,Field> _bufferMap = new HashMap<Buffer,Field>(32);
|
||||
private final HashMap<Buffer,Field> _names = new HashMap<Buffer,Field>(32);
|
||||
private final int _maxCookieVersion;
|
||||
private int _revision;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -311,6 +310,31 @@ public class HttpFields
|
|||
_maxCookieVersion=maxCookieVersion;
|
||||
}
|
||||
|
||||
|
||||
// TODO externalize this cache so it can be configurable
|
||||
private static ConcurrentMap<String, Buffer> __cache = new ConcurrentHashMap<String, Buffer>();
|
||||
/* -------------------------------------------------------------- */
|
||||
private Buffer convertValue(String value)
|
||||
{
|
||||
Buffer buffer = __cache.get(value);
|
||||
if (buffer!=null)
|
||||
return buffer;
|
||||
|
||||
try
|
||||
{
|
||||
if (__cache.size()>1000)
|
||||
__cache.clear();
|
||||
buffer = new ByteArrayBuffer(value,StringUtil.__ISO_8859_1);
|
||||
__cache.putIfAbsent(value,buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/**
|
||||
* Get enumeration of header _names. Returns an enumeration of strings representing the header
|
||||
|
@ -318,40 +342,21 @@ public class HttpFields
|
|||
*/
|
||||
public Enumeration<String> getFieldNames()
|
||||
{
|
||||
final int revision=_revision;
|
||||
final Enumeration<?> buffers = Collections.enumeration(_names.keySet());
|
||||
return new Enumeration<String>()
|
||||
{
|
||||
int i = 0;
|
||||
Field field = null;
|
||||
|
||||
public String nextElement()
|
||||
{
|
||||
return buffers.nextElement().toString();
|
||||
}
|
||||
|
||||
public boolean hasMoreElements()
|
||||
{
|
||||
if (field != null) return true;
|
||||
while (i < _fields.size())
|
||||
{
|
||||
Field f = _fields.get(i++);
|
||||
if (f != null && f._prev == null && f._revision == revision)
|
||||
{
|
||||
field = f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return buffers.hasMoreElements();
|
||||
}
|
||||
|
||||
public String nextElement() throws NoSuchElementException
|
||||
{
|
||||
if (field != null || hasMoreElements())
|
||||
{
|
||||
String n = BufferUtil.to8859_1_String(field._name);
|
||||
field = null;
|
||||
return n;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public int size()
|
||||
{
|
||||
|
@ -366,36 +371,31 @@ public class HttpFields
|
|||
*/
|
||||
public Field getField(int i)
|
||||
{
|
||||
final Field field = _fields.get(i);
|
||||
if (field._revision!=_revision)
|
||||
return null;
|
||||
return field;
|
||||
return _fields.get(i);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private Field getField(String name)
|
||||
{
|
||||
return _bufferMap.get(HttpHeaders.CACHE.lookup(name));
|
||||
return _names.get(HttpHeaders.CACHE.lookup(name));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private Field getField(Buffer name)
|
||||
{
|
||||
return _bufferMap.get(name);
|
||||
return _names.get(HttpHeaders.CACHE.lookup(name));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean containsKey(Buffer name)
|
||||
{
|
||||
Field f = getField(name);
|
||||
return (f != null && f._revision == _revision);
|
||||
return _names.containsKey(HttpHeaders.CACHE.lookup(name));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean containsKey(String name)
|
||||
{
|
||||
Field f = getField(name);
|
||||
return (f != null && f._revision == _revision);
|
||||
return _names.containsKey(HttpHeaders.CACHE.lookup(name));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -406,11 +406,8 @@ public class HttpFields
|
|||
*/
|
||||
public String getStringField(String name)
|
||||
{
|
||||
// TODO - really reuse strings from previous requests!
|
||||
Field field = getField(name);
|
||||
if (field != null && field._revision == _revision)
|
||||
return field.getValue();
|
||||
return null;
|
||||
return field==null?null:field.getValue();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -421,11 +418,8 @@ public class HttpFields
|
|||
*/
|
||||
public String getStringField(Buffer name)
|
||||
{
|
||||
// TODO - really reuse strings from previous requests!
|
||||
Field field = getField(name);
|
||||
if (field != null && field._revision == _revision)
|
||||
return BufferUtil.to8859_1_String(field._value);
|
||||
return null;
|
||||
return field==null?null:field.getValue();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -437,24 +431,24 @@ public class HttpFields
|
|||
public Buffer get(Buffer name)
|
||||
{
|
||||
Field field = getField(name);
|
||||
if (field != null && field._revision == _revision)
|
||||
return field._value;
|
||||
return null;
|
||||
return field==null?null:field._value;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/**
|
||||
* Get multi headers
|
||||
*
|
||||
* @return Enumeration of the values, or null if no such header.
|
||||
* @return Enumeration of the values
|
||||
* @param name the case-insensitive field name
|
||||
*/
|
||||
public Enumeration<String> getValues(String name)
|
||||
{
|
||||
final Field field = getField(name);
|
||||
if (field == null)
|
||||
return null;
|
||||
final int revision=_revision;
|
||||
{
|
||||
List<String> empty=Collections.emptyList();
|
||||
return Collections.enumeration(empty);
|
||||
}
|
||||
|
||||
return new Enumeration<String>()
|
||||
{
|
||||
|
@ -462,8 +456,6 @@ public class HttpFields
|
|||
|
||||
public boolean hasMoreElements()
|
||||
{
|
||||
while (f != null && f._revision != revision)
|
||||
f = f._next;
|
||||
return f != null;
|
||||
}
|
||||
|
||||
|
@ -471,9 +463,7 @@ public class HttpFields
|
|||
{
|
||||
if (f == null) throw new NoSuchElementException();
|
||||
Field n = f;
|
||||
do
|
||||
f = f._next;
|
||||
while (f != null && f._revision != revision);
|
||||
f = f._next;
|
||||
return n.getValue();
|
||||
}
|
||||
};
|
||||
|
@ -483,15 +473,17 @@ public class HttpFields
|
|||
/**
|
||||
* Get multi headers
|
||||
*
|
||||
* @return Enumeration of the value Strings, or null if no such header.
|
||||
* @return Enumeration of the value Strings
|
||||
* @param name the case-insensitive field name
|
||||
*/
|
||||
public Enumeration<String> getValues(Buffer name)
|
||||
{
|
||||
final Field field = getField(name);
|
||||
if (field == null)
|
||||
return null;
|
||||
final int revision=_revision;
|
||||
{
|
||||
List<String> empty=Collections.emptyList();
|
||||
return Collections.enumeration(empty);
|
||||
}
|
||||
|
||||
return new Enumeration<String>()
|
||||
{
|
||||
|
@ -499,8 +491,6 @@ public class HttpFields
|
|||
|
||||
public boolean hasMoreElements()
|
||||
{
|
||||
while (f != null && f._revision != revision)
|
||||
f = f._next;
|
||||
return f != null;
|
||||
}
|
||||
|
||||
|
@ -509,8 +499,6 @@ public class HttpFields
|
|||
if (f == null) throw new NoSuchElementException();
|
||||
Field n = f;
|
||||
f = f._next;
|
||||
while (f != null && f._revision != revision)
|
||||
f = f._next;
|
||||
return n.getValue();
|
||||
}
|
||||
};
|
||||
|
@ -558,6 +546,7 @@ public class HttpFields
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/**
|
||||
* Set a field.
|
||||
|
@ -567,11 +556,14 @@ public class HttpFields
|
|||
*/
|
||||
public void put(String name, String value)
|
||||
{
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = null;
|
||||
if (value != null)
|
||||
v = HttpHeaderValues.CACHE.lookup(value);
|
||||
put(n, v, -1);
|
||||
if (value==null)
|
||||
remove(name);
|
||||
else
|
||||
{
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = convertValue(value);
|
||||
put(n, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -583,8 +575,9 @@ public class HttpFields
|
|||
*/
|
||||
public void put(Buffer name, String value)
|
||||
{
|
||||
Buffer v = HttpHeaderValues.CACHE.lookup(value);
|
||||
put(name, v, -1);
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = convertValue(value);
|
||||
put(n, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -596,47 +589,19 @@ public class HttpFields
|
|||
*/
|
||||
public void put(Buffer name, Buffer value)
|
||||
{
|
||||
put(name, value, -1);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/**
|
||||
* Set a field.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @param value the value of the field. If null the field is cleared.
|
||||
* @param numValue the numeric value of the field (must match value) or -1
|
||||
*/
|
||||
public void put(Buffer name, Buffer value, long numValue)
|
||||
{
|
||||
remove(name);
|
||||
if (value == null)
|
||||
{
|
||||
remove(name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(name instanceof BufferCache.CachedBuffer)) name = HttpHeaders.CACHE.lookup(name);
|
||||
|
||||
Field field = _bufferMap.get(name);
|
||||
|
||||
// Look for value to replace.
|
||||
if (field != null)
|
||||
{
|
||||
field.reset(value, numValue, _revision);
|
||||
field = field._next;
|
||||
while (field != null)
|
||||
{
|
||||
field.clear();
|
||||
field = field._next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// new value;
|
||||
field = new Field(name, value, numValue, _revision);
|
||||
_fields.add(field);
|
||||
_bufferMap.put(field.getNameBuffer(), field);
|
||||
}
|
||||
if (!(name instanceof BufferCache.CachedBuffer))
|
||||
name = HttpHeaders.CACHE.lookup(name);
|
||||
if (!(value instanceof CachedBuffer))
|
||||
value= HttpHeaderValues.CACHE.lookup(value).asImmutableBuffer();
|
||||
|
||||
// new value;
|
||||
Field field = new Field(name, value);
|
||||
_fields.add(field);
|
||||
_names.put(name, field);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -685,9 +650,11 @@ public class HttpFields
|
|||
*/
|
||||
public void add(String name, String value) throws IllegalArgumentException
|
||||
{
|
||||
if (value==null)
|
||||
return;
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = HttpHeaderValues.CACHE.lookup(value);
|
||||
add(n, v, -1);
|
||||
Buffer v = convertValue(value);
|
||||
add(n, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -701,55 +668,34 @@ public class HttpFields
|
|||
* value.
|
||||
*/
|
||||
public void add(Buffer name, Buffer value) throws IllegalArgumentException
|
||||
{
|
||||
add(name, value, -1);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/**
|
||||
* Add to or set a field. If the field is allowed to have multiple values, add will add multiple
|
||||
* headers of the same name.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @param value the value of the field.
|
||||
* @exception IllegalArgumentException If the name is a single valued field and already has a
|
||||
* value.
|
||||
*/
|
||||
private void add(Buffer name, Buffer value, long numValue) throws IllegalArgumentException
|
||||
{
|
||||
{
|
||||
if (value == null) throw new IllegalArgumentException("null value");
|
||||
|
||||
if (!(name instanceof BufferCache.CachedBuffer)) name = HttpHeaders.CACHE.lookup(name);
|
||||
if (!(name instanceof CachedBuffer))
|
||||
name = HttpHeaders.CACHE.lookup(name);
|
||||
name=name.asImmutableBuffer();
|
||||
|
||||
Field field = _bufferMap.get(name);
|
||||
if (!(value instanceof CachedBuffer) && HttpHeaderValues.hasKnownValues(HttpHeaders.CACHE.getOrdinal(name)))
|
||||
value= HttpHeaderValues.CACHE.lookup(value);
|
||||
value=value.asImmutableBuffer();
|
||||
|
||||
Field field = _names.get(name);
|
||||
Field last = null;
|
||||
if (field != null)
|
||||
while (field != null)
|
||||
{
|
||||
while (field != null && field._revision == _revision)
|
||||
{
|
||||
last = field;
|
||||
field = field._next;
|
||||
}
|
||||
last = field;
|
||||
field = field._next;
|
||||
}
|
||||
|
||||
if (field != null)
|
||||
field.reset(value, numValue, _revision);
|
||||
// create the field
|
||||
field = new Field(name, value);
|
||||
_fields.add(field);
|
||||
|
||||
// look for chain to add too
|
||||
if (last != null)
|
||||
last._next = field;
|
||||
else
|
||||
{
|
||||
// create the field
|
||||
field = new Field(name, value, numValue, _revision);
|
||||
|
||||
// look for chain to add too
|
||||
if (last != null)
|
||||
{
|
||||
field._prev = last;
|
||||
last._next = field;
|
||||
}
|
||||
else
|
||||
_bufferMap.put(field.getNameBuffer(), field);
|
||||
|
||||
_fields.add(field);
|
||||
}
|
||||
_names.put(name, field);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -771,15 +717,13 @@ public class HttpFields
|
|||
*/
|
||||
public void remove(Buffer name)
|
||||
{
|
||||
Field field = _bufferMap.get(name);
|
||||
|
||||
if (field != null)
|
||||
if (!(name instanceof BufferCache.CachedBuffer))
|
||||
name = HttpHeaders.CACHE.lookup(name);
|
||||
Field field = _names.remove(name);
|
||||
while (field != null)
|
||||
{
|
||||
while (field != null)
|
||||
{
|
||||
field.clear();
|
||||
field = field._next;
|
||||
}
|
||||
_fields.remove(field);
|
||||
field = field._next;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -794,9 +738,7 @@ public class HttpFields
|
|||
public long getLongField(String name) throws NumberFormatException
|
||||
{
|
||||
Field field = getField(name);
|
||||
if (field != null && field._revision == _revision) return field.getLongValue();
|
||||
|
||||
return -1L;
|
||||
return field==null?-1L:field.getLongValue();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -810,8 +752,7 @@ public class HttpFields
|
|||
public long getLongField(Buffer name) throws NumberFormatException
|
||||
{
|
||||
Field field = getField(name);
|
||||
if (field != null && field._revision == _revision) return field.getLongValue();
|
||||
return -1L;
|
||||
return field==null?-1L:field.getLongValue();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -824,12 +765,9 @@ public class HttpFields
|
|||
public long getDateField(String name)
|
||||
{
|
||||
Field field = getField(name);
|
||||
if (field == null || field._revision != _revision)
|
||||
if (field == null)
|
||||
return -1;
|
||||
|
||||
if (field._numValue != -1)
|
||||
return field._numValue;
|
||||
|
||||
String val = valueParameters(BufferUtil.to8859_1_String(field._value), null);
|
||||
if (val == null)
|
||||
return -1;
|
||||
|
@ -837,7 +775,6 @@ public class HttpFields
|
|||
final long date = __dateParser.get().parse(val);
|
||||
if (date==-1)
|
||||
throw new IllegalArgumentException("Cannot convert date: " + val);
|
||||
field._numValue=date;
|
||||
return date;
|
||||
}
|
||||
|
||||
|
@ -851,7 +788,7 @@ public class HttpFields
|
|||
public void putLongField(Buffer name, long value)
|
||||
{
|
||||
Buffer v = BufferUtil.toBuffer(value);
|
||||
put(name, v, value);
|
||||
put(name, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -865,7 +802,7 @@ public class HttpFields
|
|||
{
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = BufferUtil.toBuffer(value);
|
||||
put(n, v, value);
|
||||
put(n, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -879,7 +816,7 @@ public class HttpFields
|
|||
{
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = BufferUtil.toBuffer(value);
|
||||
add(n, v, value);
|
||||
add(n, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -892,7 +829,7 @@ public class HttpFields
|
|||
public void addLongField(Buffer name, long value)
|
||||
{
|
||||
Buffer v = BufferUtil.toBuffer(value);
|
||||
add(name, v, value);
|
||||
add(name, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -906,7 +843,7 @@ public class HttpFields
|
|||
{
|
||||
String d=formatDate(date);
|
||||
Buffer v = new ByteArrayBuffer(d);
|
||||
put(name, v, date);
|
||||
put(name, v);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -934,7 +871,7 @@ public class HttpFields
|
|||
String d=formatDate(date);
|
||||
Buffer n = HttpHeaders.CACHE.lookup(name);
|
||||
Buffer v = new ByteArrayBuffer(d);
|
||||
add(n, v, date);
|
||||
add(n, v);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -984,7 +921,8 @@ public class HttpFields
|
|||
String delim=_maxCookieVersion==0?"":"\"\\\n\r\t\f\b%+ ;=";
|
||||
|
||||
// Check arguments
|
||||
if (name == null || name.length() == 0) throw new IllegalArgumentException("Bad cookie name");
|
||||
if (name == null || name.length() == 0)
|
||||
throw new IllegalArgumentException("Bad cookie name");
|
||||
|
||||
// Format value and params
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
|
@ -1053,38 +991,38 @@ public class HttpFields
|
|||
|
||||
name_value_params = buf.toString();
|
||||
|
||||
// look for existing set cookie of same name
|
||||
Field field = getField(HttpHeaders.SET_COOKIE_BUFFER);
|
||||
if (field != null)
|
||||
// remove existing set-cookie of same name
|
||||
Field field = getField(HttpHeaders.SET_COOKIE);
|
||||
Field last=null;
|
||||
while (field!=null)
|
||||
{
|
||||
final int revision=_revision;
|
||||
|
||||
while (field!=null)
|
||||
if (field._value!=null && field._value.toString().startsWith(start))
|
||||
{
|
||||
if (field._revision==revision && field._value!=null && field._value.toString().startsWith(start))
|
||||
{
|
||||
field.reset(new ByteArrayBuffer(name_value_params),-1,revision);
|
||||
name_value_params=null;
|
||||
break;
|
||||
}
|
||||
field=field._next;
|
||||
_fields.remove(field);
|
||||
if (last==null)
|
||||
_names.put(HttpHeaders.SET_COOKIE_BUFFER,field._next);
|
||||
else
|
||||
last._next=field._next;
|
||||
break;
|
||||
}
|
||||
last=field;
|
||||
field=field._next;
|
||||
}
|
||||
|
||||
if (name_value_params!=null)
|
||||
add(HttpHeaders.SET_COOKIE_BUFFER, new ByteArrayBuffer(name_value_params));
|
||||
|
||||
add(HttpHeaders.SET_COOKIE_BUFFER, new ByteArrayBuffer(name_value_params));
|
||||
|
||||
// Expire responses with set-cookie headers so they do not get cached.
|
||||
put(HttpHeaders.EXPIRES_BUFFER, __01Jan1970_BUFFER);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
public void put(Buffer buffer) throws IOException
|
||||
public void putTo(Buffer buffer) throws IOException
|
||||
{
|
||||
for (int i = 0; i < _fields.size(); i++)
|
||||
{
|
||||
Field field = _fields.get(i);
|
||||
if (field != null && field._revision == _revision) field.put(buffer);
|
||||
if (field != null)
|
||||
field.putTo(buffer);
|
||||
}
|
||||
BufferUtil.putCRLF(buffer);
|
||||
}
|
||||
|
@ -1098,7 +1036,7 @@ public class HttpFields
|
|||
for (int i = 0; i < _fields.size(); i++)
|
||||
{
|
||||
Field field = (Field) _fields.get(i);
|
||||
if (field != null && field._revision == _revision)
|
||||
if (field != null)
|
||||
{
|
||||
String tmp = field.getName();
|
||||
if (tmp != null) buffer.append(tmp);
|
||||
|
@ -1124,36 +1062,8 @@ public class HttpFields
|
|||
*/
|
||||
public void clear()
|
||||
{
|
||||
_revision++;
|
||||
if (_revision > 1000000)
|
||||
{
|
||||
_revision = 0;
|
||||
for (int i = _fields.size(); i-- > 0;)
|
||||
{
|
||||
Field field = _fields.get(i);
|
||||
if (field != null) field.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Destroy the header. Help the garbage collector by null everything that we can.
|
||||
*/
|
||||
public void destroy()
|
||||
{
|
||||
if (_fields != null)
|
||||
{
|
||||
for (int i = _fields.size(); i-- > 0;)
|
||||
{
|
||||
Field field = _fields.get(i);
|
||||
if (field != null) {
|
||||
_bufferMap.remove(field.getNameBuffer());
|
||||
field.destroy();
|
||||
}
|
||||
}
|
||||
_fields.clear();
|
||||
}
|
||||
_fields.clear();
|
||||
_names.clear();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1335,92 +1245,18 @@ public class HttpFields
|
|||
{
|
||||
private Buffer _name;
|
||||
private Buffer _value;
|
||||
private String _stringValue;
|
||||
private long _numValue;
|
||||
private Field _next;
|
||||
private Field _prev;
|
||||
private int _revision;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private Field(Buffer name, Buffer value, long numValue, int revision)
|
||||
private Field(Buffer name, Buffer value)
|
||||
{
|
||||
_name = name.asImmutableBuffer();
|
||||
_value = value.isImmutable() ? value : new View(value);
|
||||
_name = name;
|
||||
_value = value;
|
||||
_next = null;
|
||||
_prev = null;
|
||||
_revision = revision;
|
||||
_numValue = numValue;
|
||||
_stringValue=null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void clear()
|
||||
{
|
||||
_revision = -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void destroy()
|
||||
{
|
||||
_name = null;
|
||||
_value = null;
|
||||
_next = null;
|
||||
_prev = null;
|
||||
_stringValue=null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Reassign a value to this field. Checks if the string value is the same as that in the char
|
||||
* array, if so then just reuse existing value.
|
||||
*/
|
||||
private void reset(Buffer value, long numValue, int revision)
|
||||
{
|
||||
_revision = revision;
|
||||
if (_value == null)
|
||||
{
|
||||
_value = value.isImmutable() ? value : new View(value);
|
||||
_numValue = numValue;
|
||||
_stringValue=null;
|
||||
}
|
||||
else if (value.isImmutable())
|
||||
{
|
||||
_value = value;
|
||||
_numValue = numValue;
|
||||
_stringValue=null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_value instanceof View)
|
||||
((View) _value).update(value);
|
||||
else
|
||||
_value = new View(value);
|
||||
_numValue = numValue;
|
||||
|
||||
// check to see if string value is still valid.
|
||||
if (_stringValue!=null)
|
||||
{
|
||||
if (_stringValue.length()!=value.length())
|
||||
_stringValue=null;
|
||||
else
|
||||
{
|
||||
for (int i=value.length();i-->0;)
|
||||
{
|
||||
if (value.peek(value.getIndex()+i)!=_stringValue.charAt(i))
|
||||
{
|
||||
_stringValue=null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void put(Buffer buffer) throws IOException
|
||||
public void putTo(Buffer buffer) throws IOException
|
||||
{
|
||||
int o=(_name instanceof CachedBuffer)?((CachedBuffer)_name).getOrdinal():-1;
|
||||
if (o>=0)
|
||||
|
@ -1448,7 +1284,7 @@ public class HttpFields
|
|||
buffer.put((byte) ' ');
|
||||
|
||||
o=(_value instanceof CachedBuffer)?((CachedBuffer)_value).getOrdinal():-1;
|
||||
if (o>=0 || _numValue>=0)
|
||||
if (o>=0)
|
||||
buffer.put(_value);
|
||||
else
|
||||
{
|
||||
|
@ -1492,13 +1328,7 @@ public class HttpFields
|
|||
/* ------------------------------------------------------------ */
|
||||
public String getValue()
|
||||
{
|
||||
if (_stringValue==null)
|
||||
{
|
||||
_stringValue=(_value instanceof CachedBuffer)
|
||||
?_value.toString()
|
||||
:BufferUtil.to8859_1_String(_value);
|
||||
}
|
||||
return _stringValue;
|
||||
return BufferUtil.to8859_1_String(_value);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1522,14 +1352,13 @@ public class HttpFields
|
|||
/* ------------------------------------------------------------ */
|
||||
public long getLongValue()
|
||||
{
|
||||
if (_numValue == -1) _numValue = BufferUtil.toLong(_value);
|
||||
return _numValue;
|
||||
return BufferUtil.toLong(_value);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String toString()
|
||||
{
|
||||
return ("[" + (_prev == null ? "" : "<-") + getName() + "="+_revision+"=" + _value + (_next == null ? "" : "->") + "]");
|
||||
return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -523,7 +523,7 @@ public class HttpGenerator extends AbstractGenerator
|
|||
content_length = null;
|
||||
|
||||
// write the field to the header buffer
|
||||
field.put(_header);
|
||||
field.putTo(_header);
|
||||
break;
|
||||
|
||||
case HttpHeaders.CONTENT_TYPE_ORDINAL:
|
||||
|
@ -531,7 +531,7 @@ public class HttpGenerator extends AbstractGenerator
|
|||
|
||||
// write the field to the header buffer
|
||||
content_type=true;
|
||||
field.put(_header);
|
||||
field.putTo(_header);
|
||||
break;
|
||||
|
||||
case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
|
||||
|
@ -542,7 +542,7 @@ public class HttpGenerator extends AbstractGenerator
|
|||
|
||||
case HttpHeaders.CONNECTION_ORDINAL:
|
||||
if (isRequest())
|
||||
field.put(_header);
|
||||
field.putTo(_header);
|
||||
|
||||
int connection_value = field.getValueOrdinal();
|
||||
switch (connection_value)
|
||||
|
@ -601,7 +601,7 @@ public class HttpGenerator extends AbstractGenerator
|
|||
// special case for websocket connection ordering
|
||||
if (isResponse())
|
||||
{
|
||||
field.put(_header);
|
||||
field.putTo(_header);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -641,13 +641,13 @@ public class HttpGenerator extends AbstractGenerator
|
|||
if (getSendServerVersion())
|
||||
{
|
||||
has_server=true;
|
||||
field.put(_header);
|
||||
field.putTo(_header);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// write the field to the header buffer
|
||||
field.put(_header);
|
||||
field.putTo(_header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -721,7 +721,7 @@ public class HttpGenerator extends AbstractGenerator
|
|||
{
|
||||
String c = transfer_encoding.getValue();
|
||||
if (c.endsWith(HttpHeaderValues.CHUNKED))
|
||||
transfer_encoding.put(_header);
|
||||
transfer_encoding.putTo(_header);
|
||||
else
|
||||
throw new IllegalArgumentException("BAD TE");
|
||||
}
|
||||
|
|
|
@ -73,38 +73,16 @@ public class HttpHeaderValues extends BufferCache
|
|||
NO_CACHE_BUFFER=CACHE.add(NO_CACHE,NO_CACHE_ORDINAL),
|
||||
UPGRADE_BUFFER=CACHE.add(UPGRADE,UPGRADE_ORDINAL);
|
||||
|
||||
static
|
||||
{
|
||||
int index=100;
|
||||
CACHE.add("gzip",index++);
|
||||
CACHE.add("gzip,deflate",index++);
|
||||
CACHE.add("deflate",index++);
|
||||
|
||||
InputStream ua = HttpHeaderValues.class.getResourceAsStream("/org/eclipse/jetty/http/useragents");
|
||||
try
|
||||
public static boolean hasKnownValues(int httpHeaderOrdinal)
|
||||
{
|
||||
switch(httpHeaderOrdinal)
|
||||
{
|
||||
if (ua!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
LineNumberReader in = new LineNumberReader(new InputStreamReader(ua));
|
||||
String line = in.readLine();
|
||||
while (line!=null)
|
||||
{
|
||||
CACHE.add(line,index++);
|
||||
line = in.readLine();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ua.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.warn(e.toString());
|
||||
Log.debug(e);
|
||||
case HttpHeaders.CONNECTION_ORDINAL:
|
||||
case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
|
||||
case HttpHeaders.CONTENT_ENCODING_ORDINAL:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ import org.eclipse.jetty.io.BufferCache;
|
|||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class HttpHeaders extends BufferCache
|
||||
{
|
||||
|
@ -78,7 +76,10 @@ public class HttpHeaders extends BufferCache
|
|||
REFERER= "Referer",
|
||||
TE= "TE",
|
||||
USER_AGENT= "User-Agent",
|
||||
X_FORWARDED_FOR= "X-Forwarded-For";
|
||||
X_FORWARDED_FOR= "X-Forwarded-For",
|
||||
X_FORWARDED_PROTO= "X-Forwarded-Proto",
|
||||
X_FORWARDED_SERVER= "X-Forwarded-Server",
|
||||
X_FORWARDED_HOST= "X-Forwarded-Host";
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Response Fields.
|
||||
|
@ -160,7 +161,10 @@ public class HttpHeaders extends BufferCache
|
|||
MIME_VERSION_ORDINAL= 55,
|
||||
IDENTITY_ORDINAL= 56,
|
||||
CACHE_CONTROL_ORDINAL=57,
|
||||
PROXY_CONNECTION_ORDINAL=58;
|
||||
PROXY_CONNECTION_ORDINAL=58,
|
||||
X_FORWARDED_PROTO_ORDINAL=59,
|
||||
X_FORWARDED_SERVER_ORDINAL=60,
|
||||
X_FORWARDED_HOST_ORDINAL=61;
|
||||
|
||||
public final static HttpHeaders CACHE= new HttpHeaders();
|
||||
|
||||
|
@ -208,6 +212,9 @@ public class HttpHeaders extends BufferCache
|
|||
TE_BUFFER=CACHE.add(TE,TE_ORDINAL),
|
||||
USER_AGENT_BUFFER=CACHE.add(USER_AGENT,USER_AGENT_ORDINAL),
|
||||
X_FORWARDED_FOR_BUFFER=CACHE.add(X_FORWARDED_FOR,X_FORWARDED_FOR_ORDINAL),
|
||||
X_FORWARDED_PROTO_BUFFER=CACHE.add(X_FORWARDED_PROTO,X_FORWARDED_PROTO_ORDINAL),
|
||||
X_FORWARDED_SERVER_BUFFER=CACHE.add(X_FORWARDED_SERVER,X_FORWARDED_SERVER_ORDINAL),
|
||||
X_FORWARDED_HOST_BUFFER=CACHE.add(X_FORWARDED_HOST,X_FORWARDED_HOST_ORDINAL),
|
||||
ACCEPT_RANGES_BUFFER=CACHE.add(ACCEPT_RANGES,ACCEPT_RANGES_ORDINAL),
|
||||
AGE_BUFFER=CACHE.add(AGE,AGE_ORDINAL),
|
||||
ETAG_BUFFER=CACHE.add(ETAG,ETAG_ORDINAL),
|
||||
|
@ -224,4 +231,6 @@ public class HttpHeaders extends BufferCache
|
|||
MIME_VERSION_BUFFER=CACHE.add(MIME_VERSION,MIME_VERSION_ORDINAL),
|
||||
IDENTITY_BUFFER=CACHE.add(IDENTITY,IDENTITY_ORDINAL),
|
||||
PROXY_CONNECTION_BUFFER=CACHE.add(PROXY_CONNECTION,PROXY_CONNECTION_ORDINAL);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ public class HttpParser implements Parser
|
|||
private Buffer _header; // Buffer for header data (and small _content)
|
||||
private Buffer _body; // Buffer for large content
|
||||
private Buffer _buffer; // The current buffer in use (either _header or _content)
|
||||
private final View _contentView=new View(); // View of the content in the buffer for {@link Input}
|
||||
private CachedBuffer _cached;
|
||||
private View.CaseInsensitive _tok0; // Saved token: header name, request method or response version
|
||||
private View.CaseInsensitive _tok1; // Saved token: header value, request URI or response code
|
||||
|
@ -67,6 +66,7 @@ public class HttpParser implements Parser
|
|||
private boolean _forceContentBuffer;
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
protected final View _contentView=new View(); // View of the content in the buffer for {@link Input}
|
||||
protected int _state=STATE_START;
|
||||
protected byte _eol;
|
||||
protected int _length;
|
||||
|
@ -191,7 +191,7 @@ public class HttpParser implements Parser
|
|||
public void parse() throws IOException
|
||||
{
|
||||
if (_state==STATE_END)
|
||||
reset(false);
|
||||
reset();
|
||||
if (_state!=STATE_START)
|
||||
throw new IllegalStateException("!START");
|
||||
|
||||
|
@ -263,41 +263,8 @@ public class HttpParser implements Parser
|
|||
// Fill buffer if we can
|
||||
if (length == 0)
|
||||
{
|
||||
int filled=-1;
|
||||
if (_body!=null && _buffer!=_body)
|
||||
{
|
||||
_buffer=_body;
|
||||
filled=_buffer.length();
|
||||
}
|
||||
|
||||
if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity())
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL");
|
||||
long filled=fill();
|
||||
|
||||
IOException ioex=null;
|
||||
|
||||
if (_endp != null && filled<=0)
|
||||
{
|
||||
// Compress buffer if handling _content buffer
|
||||
// TODO check this is not moving data too much
|
||||
if (_buffer == _body)
|
||||
_buffer.compact();
|
||||
|
||||
if (_buffer.space() == 0)
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
try
|
||||
{
|
||||
filled=_endp.fill(_buffer);
|
||||
if (filled>0)
|
||||
progress++;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.debug(e);
|
||||
ioex=e;
|
||||
filled=-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (filled < 0)
|
||||
{
|
||||
if (_headResponse && _state>STATE_END)
|
||||
|
@ -429,8 +396,7 @@ public class HttpParser implements Parser
|
|||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
{
|
||||
// HTTP/0.9
|
||||
_handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer
|
||||
.sliceFromMark(), null);
|
||||
_handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null);
|
||||
_state=STATE_END;
|
||||
_handler.headerComplete();
|
||||
_handler.messageComplete(_contentPosition);
|
||||
|
@ -473,7 +439,7 @@ public class HttpParser implements Parser
|
|||
if (_responseStatus>0)
|
||||
_handler.startResponse(HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
|
||||
else
|
||||
_handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1,HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
|
||||
_handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
|
||||
_eol=ch;
|
||||
_state=STATE_HEADER;
|
||||
_tok0.setPutIndex(_tok0.getIndex());
|
||||
|
@ -553,13 +519,12 @@ public class HttpParser implements Parser
|
|||
_tok1.setPutIndex(_tok1.getIndex());
|
||||
_multiLineValue=null;
|
||||
}
|
||||
_buffer.setMarkIndex(-1);
|
||||
|
||||
|
||||
// now handle ch
|
||||
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
|
||||
{
|
||||
// End of header
|
||||
|
||||
// work out the _content demarcation
|
||||
if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
|
||||
{
|
||||
|
@ -583,16 +548,11 @@ public class HttpParser implements Parser
|
|||
{
|
||||
case HttpTokens.EOF_CONTENT:
|
||||
_state=STATE_EOF_CONTENT;
|
||||
if(_body==null && _buffers!=null)
|
||||
_body=_buffers.getBuffer();
|
||||
|
||||
_handler.headerComplete(); // May recurse here !
|
||||
break;
|
||||
|
||||
case HttpTokens.CHUNKED_CONTENT:
|
||||
_state=STATE_CHUNKED_CONTENT;
|
||||
if (_body==null && _buffers!=null)
|
||||
_body=_buffers.getBuffer();
|
||||
_handler.headerComplete(); // May recurse here !
|
||||
break;
|
||||
|
||||
|
@ -604,9 +564,6 @@ public class HttpParser implements Parser
|
|||
|
||||
default:
|
||||
_state=STATE_CONTENT;
|
||||
if(_forceContentBuffer ||
|
||||
(_buffers!=null && _body==null && _buffer==_header && _contentLength>=(_header.capacity()-_header.getIndex())))
|
||||
_body=_buffers.getBuffer();
|
||||
_handler.headerComplete(); // May recurse here !
|
||||
break;
|
||||
}
|
||||
|
@ -930,43 +887,54 @@ public class HttpParser implements Parser
|
|||
*/
|
||||
public long fill() throws IOException
|
||||
{
|
||||
// Do we have a buffer?
|
||||
if (_buffer==null)
|
||||
{
|
||||
_buffer=_header=getHeaderBuffer();
|
||||
_tok0=new View.CaseInsensitive(_buffer);
|
||||
_tok1=new View.CaseInsensitive(_buffer);
|
||||
}
|
||||
if (_body!=null && _buffer!=_body)
|
||||
_buffer=_body;
|
||||
if (_buffer == _body)
|
||||
//noinspection ConstantConditions
|
||||
_buffer.compact();
|
||||
|
||||
int space=_buffer.space();
|
||||
|
||||
// Fill buffer if we can
|
||||
if (space == 0)
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
else
|
||||
// Is there unconsumed content in body buffer
|
||||
if (_state>STATE_END && _buffer==_header && _body!=null && _body.hasContent() && _header!=null && !_header.hasContent())
|
||||
{
|
||||
int filled=-1;
|
||||
|
||||
if (_endp != null )
|
||||
_buffer=_body;
|
||||
return _buffer.length();
|
||||
}
|
||||
|
||||
// Do we need a body buffer?
|
||||
if (_buffer==_header && _state>STATE_END && (_forceContentBuffer || _header.space()==0) && (_body!=null||_buffers!=null))
|
||||
{
|
||||
if (_body==null)
|
||||
_body=_buffers.getBuffer();
|
||||
_buffer=_body;
|
||||
}
|
||||
|
||||
// Do we have somewhere to fill from?
|
||||
if (_endp != null )
|
||||
{
|
||||
// Shall we compact the body?
|
||||
if (_buffer==_body || _state>STATE_END)
|
||||
{
|
||||
try
|
||||
{
|
||||
filled=_endp.fill(_buffer);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.debug(e);
|
||||
reset(true);
|
||||
throw (e instanceof EofException) ? e:new EofException(e);
|
||||
}
|
||||
_buffer.compact();
|
||||
}
|
||||
|
||||
return filled;
|
||||
// Are we full?
|
||||
if (_buffer.space() == 0)
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
|
||||
try
|
||||
{
|
||||
return _endp.fill(_buffer);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.debug(e);
|
||||
throw (e instanceof EofException) ? e:new EofException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -1003,69 +971,74 @@ public class HttpParser implements Parser
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void reset(boolean returnBuffers)
|
||||
public void reset()
|
||||
{
|
||||
// reset state
|
||||
_contentView.setGetIndex(_contentView.putIndex());
|
||||
|
||||
_state=STATE_START;
|
||||
_contentLength=HttpTokens.UNKNOWN_CONTENT;
|
||||
_contentPosition=0;
|
||||
_length=0;
|
||||
_responseStatus=0;
|
||||
|
||||
// Consume LF if CRLF
|
||||
if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer!=null && _buffer.hasContent() && _buffer.peek() == HttpTokens.LINE_FEED)
|
||||
_eol=_buffer.get();
|
||||
|
||||
if (_body!=null)
|
||||
{
|
||||
if (_body.hasContent())
|
||||
if (_body!=null && _body.hasContent())
|
||||
{
|
||||
// There is content in the body after the end of the request.
|
||||
// This is probably a pipelined header of the next request, so we need to
|
||||
// copy it to the header buffer.
|
||||
if (_header==null)
|
||||
{
|
||||
// There is content in the body after the end of the request.
|
||||
// This is probably a pipelined header of the next request, so we need to
|
||||
// copy it to the header buffer.
|
||||
_header.setMarkIndex(-1);
|
||||
_header.compact();
|
||||
int take=_header.space();
|
||||
if (take>_body.length())
|
||||
take=_body.length();
|
||||
_body.peek(_body.getIndex(),take);
|
||||
_body.skip(_header.put(_body.peek(_body.getIndex(),take)));
|
||||
}
|
||||
|
||||
if (_body.length()==0)
|
||||
{
|
||||
if (_buffers!=null && returnBuffers)
|
||||
_buffers.returnBuffer(_body);
|
||||
_body=null;
|
||||
_header=_buffers.getHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
_body.setMarkIndex(-1);
|
||||
_body.compact();
|
||||
_header.setMarkIndex(-1);
|
||||
_header.compact();
|
||||
}
|
||||
int take=_header.space();
|
||||
if (take>_body.length())
|
||||
take=_body.length();
|
||||
_body.peek(_body.getIndex(),take);
|
||||
_body.skip(_header.put(_body.peek(_body.getIndex(),take)));
|
||||
}
|
||||
|
||||
if (_header!=null)
|
||||
{
|
||||
_header.setMarkIndex(-1);
|
||||
if (!_header.hasContent() && _buffers!=null && returnBuffers)
|
||||
{
|
||||
_buffers.returnBuffer(_header);
|
||||
_header=null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_header.compact();
|
||||
_tok0.update(_header);
|
||||
_tok0.update(0,0);
|
||||
_tok1.update(_header);
|
||||
_tok1.update(0,0);
|
||||
}
|
||||
_header.compact();
|
||||
}
|
||||
if (_body!=null)
|
||||
_body.setMarkIndex(-1);
|
||||
|
||||
_buffer=_header;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void returnBuffers()
|
||||
{
|
||||
if (_body!=null && !_body.hasContent() && _body.markIndex()==-1)
|
||||
{
|
||||
if (_buffer==_body)
|
||||
_buffer=_header;
|
||||
if (_buffers!=null)
|
||||
_buffers.returnBuffer(_body);
|
||||
_body=null;
|
||||
}
|
||||
|
||||
if (_header!=null && !_header.hasContent() && _header.markIndex()==-1)
|
||||
{
|
||||
if (_buffer==_header)
|
||||
_buffer=null;
|
||||
_buffers.returnBuffer(_header);
|
||||
_header=null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void setState(int state)
|
||||
{
|
||||
|
@ -1111,8 +1084,6 @@ public class HttpParser implements Parser
|
|||
_forceContentBuffer=force;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer blockForContent(long maxIdleTime) throws IOException
|
||||
{
|
||||
|
@ -1121,50 +1092,33 @@ public class HttpParser implements Parser
|
|||
if (getState() <= HttpParser.STATE_END)
|
||||
return null;
|
||||
|
||||
// Handle simple end points.
|
||||
if (_endp==null)
|
||||
parseNext();
|
||||
|
||||
// Handle blocking end points
|
||||
else if (_endp.isBlocking())
|
||||
{
|
||||
try
|
||||
{
|
||||
parseNext();
|
||||
|
||||
// parse until some progress is made (or IOException thrown for timeout)
|
||||
while(_contentView.length() == 0 && !isState(HttpParser.STATE_END) && _endp.isOpen())
|
||||
{
|
||||
// Try to get more _parser._content
|
||||
parseNext();
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
_endp.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
else // Handle non-blocking end point
|
||||
try
|
||||
{
|
||||
parseNext();
|
||||
|
||||
// parse until some progress is made (or IOException thrown for timeout)
|
||||
while(_contentView.length() == 0 && !isState(HttpParser.STATE_END) && _endp.isOpen())
|
||||
while(_contentView.length() == 0 && !isState(HttpParser.STATE_END) && _endp!=null && _endp.isOpen())
|
||||
{
|
||||
if (_endp.isBufferingInput() && parseNext()>0)
|
||||
continue;
|
||||
|
||||
if (!_endp.blockReadable(maxIdleTime))
|
||||
if (!_endp.isBlocking())
|
||||
{
|
||||
_endp.close();
|
||||
throw new EofException("timeout");
|
||||
if (_endp.isBufferingInput() && parseNext()>0)
|
||||
continue;
|
||||
|
||||
if (!_endp.blockReadable(maxIdleTime))
|
||||
{
|
||||
_endp.close();
|
||||
throw new EofException("timeout");
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get more _parser._content
|
||||
parseNext();
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
_endp.close();
|
||||
throw e;
|
||||
}
|
||||
|
||||
return _contentView.length()>0?_contentView:null;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ import java.io.IOException;
|
|||
*/
|
||||
public interface Parser
|
||||
{
|
||||
void reset(boolean returnBuffers);
|
||||
void returnBuffers();
|
||||
void reset();
|
||||
|
||||
boolean isComplete();
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; GTB5; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
|
||||
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
|
||||
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
|
||||
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.20) Gecko/20081217 Firefox/2.0.0.20
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.2)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; Media Center PC 5.0)
|
||||
msnbot/1.1 (+http://search.msn.com/msnbot.htm)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; FunWebProducts; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
|
||||
Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)
|
||||
FeedBurner/1.0 (http://www.FeedBurner.com)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.1)
|
||||
Mozilla/5.0 (Twiceler-0.9 http://www.cuil.com/twiceler/robot.html)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; InfoPath.2)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; InfoPath.1)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0)
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; InfoPath.1)
|
||||
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 1.1.4322)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.2)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)
|
||||
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)
|
||||
Jakarta Commons-HttpClient/2.0.2
|
||||
Java/1.6.0
|
||||
|
|
@ -63,7 +63,21 @@ public class HttpFieldsTest
|
|||
assertEquals(e.nextElement(), "value0");
|
||||
assertEquals(false, e.hasMoreElements());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet() throws Exception
|
||||
{
|
||||
HttpFields header = new HttpFields();
|
||||
|
||||
header.put("name0", "value0");
|
||||
header.put(new ByteArrayBuffer("name1"), new ByteArrayBuffer("value1"));
|
||||
|
||||
assertEquals("value0",header.getStringField("name0"));
|
||||
assertEquals("value0",header.getStringField("Name0"));
|
||||
assertEquals("value1",header.getStringField("name1"));
|
||||
assertEquals("value1",header.getStringField("Name1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCRLF() throws Exception
|
||||
{
|
||||
|
@ -74,7 +88,7 @@ public class HttpFieldsTest
|
|||
header.put("name:2", "value:\r\n2");
|
||||
|
||||
ByteArrayBuffer buffer = new ByteArrayBuffer(1024);
|
||||
header.put(buffer);
|
||||
header.putTo(buffer);
|
||||
assertTrue(buffer.toString().contains("name0: value0"));
|
||||
assertTrue(buffer.toString().contains("name1: value1"));
|
||||
assertTrue(buffer.toString().contains("name2: value:2"));
|
||||
|
@ -122,10 +136,10 @@ public class HttpFieldsTest
|
|||
assertNull(header.getStringField("name3"));
|
||||
|
||||
int matches=0;
|
||||
Enumeration e = header.getFieldNames();
|
||||
Enumeration<String> e = header.getFieldNames();
|
||||
while (e.hasMoreElements())
|
||||
{
|
||||
Object o=e.nextElement();
|
||||
String o=e.nextElement();
|
||||
if ("name0".equals(o))
|
||||
matches++;
|
||||
if ("name1".equals(o))
|
||||
|
@ -269,21 +283,6 @@ public class HttpFieldsTest
|
|||
assertEquals(false, e.hasMoreElements());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroy() throws Exception
|
||||
{
|
||||
HttpFields header = new HttpFields();
|
||||
|
||||
header.put(new ByteArrayBuffer("name0"), new View(new ByteArrayBuffer("value0")));
|
||||
assertTrue(header.getFieldNames().hasMoreElements());
|
||||
assertNotNull(header.getStringField("name0"));
|
||||
assertNull(header.getStringField("name1"));
|
||||
|
||||
header.destroy();
|
||||
|
||||
assertNull(header.getStringField("name0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCase() throws Exception
|
||||
{
|
||||
|
@ -344,7 +343,6 @@ public class HttpFieldsTest
|
|||
assertTrue(s.contains("message-id"));
|
||||
assertEquals("value",fields.getStringField("Message-ID").toLowerCase());
|
||||
assertEquals("value",fields.getStringField("message-id").toLowerCase());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -352,7 +350,6 @@ public class HttpFieldsTest
|
|||
{
|
||||
assertTrue(((CachedBuffer)HttpHeaderValues.CACHE.lookup("unknown value")).getOrdinal()<0);
|
||||
assertTrue(((CachedBuffer)HttpHeaderValues.CACHE.lookup("close")).getOrdinal()>=0);
|
||||
assertTrue(((CachedBuffer)HttpHeaderValues.CACHE.lookup("Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)")).getOrdinal()>=0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -77,7 +77,7 @@ public abstract class AbstractBuffer implements Buffer
|
|||
public ByteArrayBuffer duplicate(int access)
|
||||
{
|
||||
Buffer b=this.buffer();
|
||||
if (b instanceof Buffer.CaseInsensitve)
|
||||
if (this instanceof Buffer.CaseInsensitve || b instanceof Buffer.CaseInsensitve)
|
||||
return new ByteArrayBuffer.CaseInsensitive(asArray(), 0, length(),access);
|
||||
else
|
||||
return new ByteArrayBuffer(asArray(), 0, length(), access);
|
||||
|
|
|
@ -66,12 +66,15 @@ public class BufferCache
|
|||
|
||||
public Buffer lookup(Buffer buffer)
|
||||
{
|
||||
if (buffer instanceof CachedBuffer)
|
||||
return buffer;
|
||||
|
||||
Buffer b= get(buffer);
|
||||
if (b == null)
|
||||
{
|
||||
if (buffer instanceof Buffer.CaseInsensitve)
|
||||
return buffer;
|
||||
return new View.CaseInsensitive(buffer);
|
||||
return new ByteArrayBuffer.CaseInsensitive(buffer.asArray(),0,buffer.length(),Buffer.IMMUTABLE);
|
||||
}
|
||||
|
||||
return b;
|
||||
|
|
|
@ -26,11 +26,11 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
*/
|
||||
public class ByteArrayBuffer extends AbstractBuffer
|
||||
{
|
||||
protected byte[] _bytes;
|
||||
final protected byte[] _bytes;
|
||||
|
||||
protected ByteArrayBuffer(int access, boolean isVolatile)
|
||||
protected ByteArrayBuffer(int size, int access, boolean isVolatile)
|
||||
{
|
||||
super(access, isVolatile);
|
||||
this(new byte[size],0,0,access, isVolatile);
|
||||
}
|
||||
|
||||
public ByteArrayBuffer(byte[] bytes)
|
||||
|
@ -63,7 +63,7 @@ public class ByteArrayBuffer extends AbstractBuffer
|
|||
|
||||
public ByteArrayBuffer(int size)
|
||||
{
|
||||
this(new byte[size], 0, size, READWRITE);
|
||||
this(new byte[size], 0, 0, READWRITE);
|
||||
setPutIndex(0);
|
||||
}
|
||||
|
||||
|
@ -332,38 +332,7 @@ public class ByteArrayBuffer extends AbstractBuffer
|
|||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Wrap a byte array.
|
||||
* @param b
|
||||
* @param off
|
||||
* @param len
|
||||
*/
|
||||
public void wrap(byte[] b, int off, int len)
|
||||
{
|
||||
if (b==null)
|
||||
throw new IllegalArgumentException();
|
||||
if (isReadOnly()) throw new IllegalStateException(__READONLY);
|
||||
if (isImmutable()) throw new IllegalStateException(__IMMUTABLE);
|
||||
_bytes=b;
|
||||
clear();
|
||||
setGetIndex(off);
|
||||
setPutIndex(off+len);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Wrap a byte array
|
||||
* @param b
|
||||
*/
|
||||
public void wrap(byte[] b)
|
||||
{
|
||||
if (isReadOnly()) throw new IllegalStateException(__READONLY);
|
||||
if (isImmutable()) throw new IllegalStateException(__IMMUTABLE);
|
||||
_bytes=b;
|
||||
setGetIndex(0);
|
||||
setPutIndex(b.length);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void writeTo(OutputStream out)
|
||||
|
@ -420,7 +389,7 @@ public class ByteArrayBuffer extends AbstractBuffer
|
|||
{
|
||||
super(s);
|
||||
}
|
||||
|
||||
|
||||
public CaseInsensitive(byte[] b, int o, int l, int rw)
|
||||
{
|
||||
super(b,o,l,rw);
|
||||
|
|
|
@ -18,22 +18,21 @@ import org.eclipse.jetty.io.ByteArrayBuffer;
|
|||
|
||||
public class IndirectNIOBuffer extends ByteArrayBuffer implements NIOBuffer
|
||||
{
|
||||
protected ByteBuffer _buf;
|
||||
protected final ByteBuffer _buf;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public IndirectNIOBuffer(int size)
|
||||
{
|
||||
super(READWRITE,NON_VOLATILE);
|
||||
_buf = ByteBuffer.allocate(size);
|
||||
super(size,READWRITE,NON_VOLATILE);
|
||||
_buf = ByteBuffer.wrap(_bytes);
|
||||
_buf.position(0);
|
||||
_buf.limit(_buf.capacity());
|
||||
_bytes=_buf.array();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public IndirectNIOBuffer(ByteBuffer buffer,boolean immutable)
|
||||
{
|
||||
super(immutable?IMMUTABLE:READWRITE,NON_VOLATILE);
|
||||
super(buffer.array(),0,0, immutable?IMMUTABLE:READWRITE,NON_VOLATILE);
|
||||
if (buffer.isDirect())
|
||||
throw new IllegalArgumentException();
|
||||
_buf = buffer;
|
||||
|
@ -41,7 +40,6 @@ public class IndirectNIOBuffer extends ByteArrayBuffer implements NIOBuffer
|
|||
_put=buffer.limit();
|
||||
buffer.position(0);
|
||||
buffer.limit(buffer.capacity());
|
||||
_bytes=_buf.array();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.io.Connection;
|
|||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -33,6 +34,8 @@ import org.eclipse.jetty.util.log.Log;
|
|||
*/
|
||||
public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPoint, ConnectedEndPoint
|
||||
{
|
||||
public static final Logger __log=Log.getLogger("org.eclipse.jetty.io.nio");
|
||||
|
||||
private final SelectorManager.SelectSet _selectSet;
|
||||
private final SelectorManager _manager;
|
||||
private final Runnable _handler = new Runnable()
|
||||
|
@ -196,7 +199,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
if(!dispatched)
|
||||
{
|
||||
_dispatched = false;
|
||||
Log.warn("Dispatched Failed! "+this+" to "+_manager);
|
||||
__log.warn("Dispatched Failed! "+this+" to "+_manager);
|
||||
updateKey();
|
||||
}
|
||||
}
|
||||
|
@ -330,7 +333,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
Log.warn(e);
|
||||
__log.warn(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -375,7 +378,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
Log.warn(e);
|
||||
__log.warn(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -388,7 +391,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
catch(Throwable e)
|
||||
{
|
||||
// TODO remove this if it finds nothing
|
||||
Log.warn(e);
|
||||
__log.warn(e);
|
||||
if (e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
if (e instanceof Error)
|
||||
|
@ -435,7 +438,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
catch(Exception e)
|
||||
{
|
||||
_key=null;
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +476,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
if (_key!=null && _key.isValid())
|
||||
{
|
||||
_key.cancel();
|
||||
|
@ -535,7 +538,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
final Connection next = _connection.handle();
|
||||
if (next!=_connection)
|
||||
{
|
||||
Log.debug("{} replaced {}",next,_connection);
|
||||
__log.debug("{} replaced {}",next,_connection);
|
||||
_connection=next;
|
||||
continue;
|
||||
}
|
||||
|
@ -544,26 +547,26 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
}
|
||||
catch (ClosedChannelException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
catch (EofException e)
|
||||
{
|
||||
Log.debug("EOF", e);
|
||||
__log.debug("EOF", e);
|
||||
try{close();}
|
||||
catch(IOException e2){Log.ignore(e2);}
|
||||
catch(IOException e2){__log.ignore(e2);}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.warn(e.toString());
|
||||
Log.debug(e);
|
||||
__log.warn(e.toString());
|
||||
__log.debug(e);
|
||||
try{close();}
|
||||
catch(IOException e2){Log.ignore(e2);}
|
||||
catch(IOException e2){__log.ignore(e2);}
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
Log.warn("handle failed", e);
|
||||
__log.warn("handle failed", e);
|
||||
try{close();}
|
||||
catch(IOException e2){Log.ignore(e2);}
|
||||
catch(IOException e2){__log.ignore(e2);}
|
||||
}
|
||||
dispatched=!undispatch();
|
||||
}
|
||||
|
@ -575,7 +578,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
dispatched=!undispatch();
|
||||
while (dispatched)
|
||||
{
|
||||
Log.warn("SCEP.run() finally DISPATCHED");
|
||||
__log.warn("SCEP.run() finally DISPATCHED");
|
||||
dispatched=!undispatch();
|
||||
}
|
||||
}
|
||||
|
@ -595,7 +598,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.util.concurrent.ConcurrentMap;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.management.RuntimeErrorException;
|
||||
|
||||
import org.eclipse.jetty.io.ConnectedEndPoint;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
|
@ -40,6 +39,7 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
|||
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Timeout;
|
||||
import org.eclipse.jetty.util.thread.Timeout.Task;
|
||||
|
||||
|
@ -54,6 +54,8 @@ import org.eclipse.jetty.util.thread.Timeout.Task;
|
|||
*/
|
||||
public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
|
||||
{
|
||||
public static final Logger __log=Log.getLogger("org.eclipse.jetty.io.nio");
|
||||
|
||||
// TODO Tune these by approx system speed.
|
||||
private static final int __JVMBUG_THRESHHOLD=Integer.getInteger("org.eclipse.jetty.io.nio.JVMBUG_THRESHHOLD",0).intValue();
|
||||
private static final int __MONITOR_PERIOD=Integer.getInteger("org.eclipse.jetty.io.nio.MONITOR_PERIOD",1000).intValue();
|
||||
|
@ -295,8 +297,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
protected void connectionFailed(SocketChannel channel,Throwable ex,Object attachment)
|
||||
{
|
||||
Log.warn(ex+","+channel+","+attachment);
|
||||
Log.debug(ex);
|
||||
__log.warn(ex+","+channel+","+attachment);
|
||||
__log.debug(ex);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -447,7 +449,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch (CancelledKeyException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
|
@ -455,9 +457,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
throw (ThreadDeath)e;
|
||||
|
||||
if (isRunning())
|
||||
Log.warn(e);
|
||||
__log.warn(e);
|
||||
else
|
||||
Log.debug(e);
|
||||
__log.debug(e);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -465,7 +467,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(IOException e2)
|
||||
{
|
||||
Log.debug(e2);
|
||||
__log.debug(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -489,7 +491,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
now=System.currentTimeMillis();
|
||||
}
|
||||
|
@ -583,14 +585,14 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch (CancelledKeyException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (isRunning())
|
||||
Log.warn(e);
|
||||
__log.warn(e);
|
||||
else
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -599,7 +601,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(IOException e2)
|
||||
{
|
||||
Log.debug(e2);
|
||||
__log.debug(e2);
|
||||
}
|
||||
|
||||
if (key != null && !(key.channel() instanceof ServerSocketChannel) && key.isValid())
|
||||
|
@ -644,13 +646,13 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
catch (ClosedSelectorException e)
|
||||
{
|
||||
if (isRunning())
|
||||
Log.warn(e);
|
||||
__log.warn(e);
|
||||
else
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
catch (CancelledKeyException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -685,16 +687,16 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
if (now>_log)
|
||||
{
|
||||
if (_paused>0)
|
||||
Log.debug(this+" Busy selector - injecting delay "+_paused+" times");
|
||||
__log.debug(this+" Busy selector - injecting delay "+_paused+" times");
|
||||
|
||||
if (_jvmFix2>0)
|
||||
Log.debug(this+" JVM BUG(s) - injecting delay"+_jvmFix2+" times");
|
||||
__log.debug(this+" JVM BUG(s) - injecting delay"+_jvmFix2+" times");
|
||||
|
||||
if (_jvmFix1>0)
|
||||
Log.debug(this+" JVM BUG(s) - recreating selector "+_jvmFix1+" times, cancelled keys "+_jvmFix0+" times");
|
||||
__log.debug(this+" JVM BUG(s) - recreating selector "+_jvmFix1+" times, cancelled keys "+_jvmFix0+" times");
|
||||
|
||||
else if(Log.isDebugEnabled() && _jvmFix0>0)
|
||||
Log.debug(this+" JVM BUG(s) - cancelled keys "+_jvmFix0+" times");
|
||||
else if(__log.isDebugEnabled() && _jvmFix0>0)
|
||||
__log.debug(this+" JVM BUG(s) - cancelled keys "+_jvmFix0+" times");
|
||||
_paused=0;
|
||||
_jvmFix2=0;
|
||||
_jvmFix1=0;
|
||||
|
@ -718,7 +720,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
}
|
||||
else if (_jvmBug==__JVMBUG_THRESHHOLD)
|
||||
|
@ -752,7 +754,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
if (++_busyKeyCount>__BUSY_KEY && !(busy.channel() instanceof ServerSocketChannel))
|
||||
{
|
||||
final SelectChannelEndPoint endpoint = (SelectChannelEndPoint)busy.attachment();
|
||||
Log.warn("Busy Key "+busy.channel()+" "+endpoint);
|
||||
__log.warn("Busy Key "+busy.channel()+" "+endpoint);
|
||||
busy.cancel();
|
||||
if (endpoint!=null)
|
||||
{
|
||||
|
@ -766,7 +768,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -905,7 +907,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
|
||||
// close endpoints and selector
|
||||
|
@ -925,7 +927,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -939,7 +941,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
_selector=null;
|
||||
}
|
||||
|
@ -990,7 +992,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
__log.ignore(e);
|
||||
}
|
||||
AggregateLifeCycle.dump(out,indent,dump);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
*/
|
||||
public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
||||
{
|
||||
static Logger __log = Log.getLogger("org.eclipse.jetty.http.ssl");
|
||||
|
||||
public static final Logger __log=Log.getLogger("org.eclipse.jetty.io.nio").getLogger("ssl");
|
||||
|
||||
private static final ByteBuffer[] __NO_BUFFERS={};
|
||||
|
||||
private final Buffers _buffers;
|
||||
|
@ -325,7 +325,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
*/
|
||||
@Override
|
||||
public int fill(Buffer buffer) throws IOException
|
||||
{
|
||||
{
|
||||
// This end point only works on NIO buffer type (director
|
||||
// or indirect), so extract the NIO buffer that is wrapped
|
||||
// by the passed jetty Buffer.
|
||||
|
|
|
@ -25,7 +25,11 @@ public class NestedParser implements Parser
|
|||
{
|
||||
}
|
||||
|
||||
public void reset(boolean returnBuffers)
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
public void returnBuffers()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -69,10 +69,10 @@ public abstract class AbstractConnector extends HttpBuffers implements Connector
|
|||
private boolean _forwarded;
|
||||
private String _hostHeader;
|
||||
|
||||
private String _forwardedHostHeader = "X-Forwarded-Host";
|
||||
private String _forwardedServerHeader = "X-Forwarded-Server";
|
||||
private String _forwardedForHeader = "X-Forwarded-For";
|
||||
private String _forwardedProtoHeader = "X-Forwarded-Proto";
|
||||
private String _forwardedHostHeader = HttpHeaders.X_FORWARDED_HOST;
|
||||
private String _forwardedServerHeader = HttpHeaders.X_FORWARDED_SERVER;
|
||||
private String _forwardedForHeader = HttpHeaders.X_FORWARDED_FOR;
|
||||
private String _forwardedProtoHeader = HttpHeaders.X_FORWARDED_PROTO;
|
||||
private String _forwardedCipherSuiteHeader;
|
||||
private String _forwardedSslSessionIdHeader;
|
||||
private boolean _reuseAddress = true;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.http.HttpException;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
||||
public class AsyncHttpConnection extends HttpConnection
|
||||
{
|
||||
public AsyncHttpConnection(Connector connector, EndPoint endpoint, Server server)
|
||||
{
|
||||
super(connector,endpoint,server);
|
||||
}
|
||||
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
Connection connection = this;
|
||||
|
||||
// Loop while more in buffer
|
||||
try
|
||||
{
|
||||
setCurrentConnection(this);
|
||||
|
||||
boolean progress=true;
|
||||
boolean more_in_buffer =false;
|
||||
|
||||
while (_endp.isOpen() && (more_in_buffer || progress))
|
||||
{
|
||||
progress=false;
|
||||
try
|
||||
{
|
||||
Log.debug("async request",_request);
|
||||
|
||||
// Handle resumed request
|
||||
if (_request._async.isAsync() && !_request._async.isComplete())
|
||||
handleRequest();
|
||||
|
||||
// else Parse more input
|
||||
else if (!_parser.isComplete() && _parser.parseAvailable()>0)
|
||||
progress=true;
|
||||
|
||||
// Generate more output
|
||||
if (_generator.isCommitted() && !_generator.isComplete() && _generator.flushBuffer()>0)
|
||||
progress=true;
|
||||
|
||||
// Flush output from buffering endpoint
|
||||
if (_endp.isBufferingOutput())
|
||||
_endp.flush();
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
if (Log.isDebugEnabled())
|
||||
{
|
||||
Log.debug("uri="+_uri);
|
||||
Log.debug("fields="+_requestFields);
|
||||
Log.debug(e);
|
||||
}
|
||||
_generator.sendError(e.getStatus(), e.getReason(), null, true);
|
||||
_parser.reset();
|
||||
_endp.close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Do we need to complete a half close?
|
||||
if (_endp.isInputShutdown() && (_parser.isIdle() || _parser.isComplete()))
|
||||
{
|
||||
Log.debug("complete half close {}",this);
|
||||
more_in_buffer=false;
|
||||
_endp.close();
|
||||
reset(true);
|
||||
}
|
||||
|
||||
// else Is this request/response round complete?
|
||||
else if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
|
||||
{
|
||||
// look for a switched connection instance?
|
||||
if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||
{
|
||||
Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
|
||||
if (switched!=null)
|
||||
{
|
||||
_parser.reset();
|
||||
_generator.reset(true);
|
||||
return switched;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the parser/generator
|
||||
// keep the buffers as we will cycle
|
||||
progress=true;
|
||||
reset(false);
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
}
|
||||
|
||||
// else Are we suspended?
|
||||
else if (_request.isAsyncStarted())
|
||||
{
|
||||
Log.debug("suspended {}",this);
|
||||
more_in_buffer=false;
|
||||
progress=false;
|
||||
}
|
||||
else
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
setCurrentConnection(null);
|
||||
_parser.returnBuffers();
|
||||
|
||||
// Are we write blocked
|
||||
if (_generator.isCommitted() && !_generator.isComplete())
|
||||
((AsyncEndPoint)_endp).scheduleWrite();
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.http.Generator;
|
||||
import org.eclipse.jetty.http.HttpException;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.Parser;
|
||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
||||
public class BlockingHttpConnection extends HttpConnection
|
||||
{
|
||||
private volatile boolean _handling;
|
||||
|
||||
public BlockingHttpConnection(Connector connector, EndPoint endpoint, Server server)
|
||||
{
|
||||
super(connector,endpoint,server);
|
||||
}
|
||||
|
||||
|
||||
public BlockingHttpConnection(Connector connector, EndPoint endpoint, Server server, Parser parser, Generator generator, Request request)
|
||||
{
|
||||
super(connector,endpoint,server,parser,generator,request);
|
||||
}
|
||||
|
||||
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
// TODO - simplify this - as much of it is taken from the async case and does not apply to blocking.
|
||||
|
||||
|
||||
Connection connection = this;
|
||||
|
||||
// Loop while more in buffer
|
||||
boolean more_in_buffer =true; // assume true until proven otherwise
|
||||
boolean progress=true;
|
||||
|
||||
try
|
||||
{
|
||||
assert getCurrentConnection()==null;
|
||||
assert _handling==false;
|
||||
_handling=true;
|
||||
setCurrentConnection(this);
|
||||
|
||||
while (more_in_buffer && _endp.isOpen())
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we are not ended then parse available
|
||||
if (!_parser.isComplete())
|
||||
{
|
||||
int parsed=_parser.parseAvailable();
|
||||
if (parsed>0)
|
||||
progress=true;
|
||||
}
|
||||
|
||||
// Do we have more generating to do?
|
||||
// Loop here because some writes may take multiple steps and
|
||||
// we need to flush them all before potentially blocking in the
|
||||
// next loop.
|
||||
while (_generator.isCommitted() && !_generator.isComplete())
|
||||
{
|
||||
long written=_generator.flushBuffer();
|
||||
if (written<=0)
|
||||
break;
|
||||
progress=true;
|
||||
if (_endp.isBufferingOutput())
|
||||
_endp.flush();
|
||||
}
|
||||
|
||||
// Flush buffers
|
||||
if (_endp.isBufferingOutput())
|
||||
{
|
||||
_endp.flush();
|
||||
if (!_endp.isBufferingOutput())
|
||||
progress=true;
|
||||
}
|
||||
|
||||
if (!progress)
|
||||
return this;
|
||||
progress=false;
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
if (Log.isDebugEnabled())
|
||||
{
|
||||
Log.debug("uri="+_uri);
|
||||
Log.debug("fields="+_requestFields);
|
||||
Log.debug(e);
|
||||
}
|
||||
_generator.sendError(e.getStatus(), e.getReason(), null, true);
|
||||
|
||||
_parser.reset();
|
||||
_endp.close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
|
||||
// Is this request/response round complete?
|
||||
if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
|
||||
{
|
||||
// look for a switched connection instance?
|
||||
Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||
?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null;
|
||||
|
||||
// have we switched?
|
||||
if (switched!=null)
|
||||
{
|
||||
_parser.reset();
|
||||
_generator.reset(true);
|
||||
connection=switched;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No switch, so cleanup and reset
|
||||
if (!_generator.isPersistent() || _endp.isInputShutdown())
|
||||
{
|
||||
_parser.reset();
|
||||
more_in_buffer=false;
|
||||
_endp.close();
|
||||
}
|
||||
|
||||
if (more_in_buffer)
|
||||
{
|
||||
reset(false);
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
}
|
||||
else
|
||||
reset(true);
|
||||
progress=true;
|
||||
}
|
||||
}
|
||||
else if (_parser.isIdle() && _endp.isInputShutdown())
|
||||
{
|
||||
more_in_buffer=false;
|
||||
_endp.close();
|
||||
}
|
||||
|
||||
if (_request.isAsyncStarted())
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint)
|
||||
((AsyncEndPoint)_endp).scheduleWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_parser.returnBuffers();
|
||||
setCurrentConnection(null);
|
||||
_handling=false;
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
|
@ -94,7 +94,6 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<HttpConnection>();
|
||||
|
||||
private int _requests;
|
||||
private volatile boolean _handling;
|
||||
|
||||
protected final Connector _connector;
|
||||
protected final Server _server;
|
||||
|
@ -363,158 +362,6 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
return _generator.isCommitted();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
Connection connection = this;
|
||||
|
||||
// Loop while more in buffer
|
||||
boolean more_in_buffer =true; // assume true until proven otherwise
|
||||
boolean progress=true;
|
||||
|
||||
try
|
||||
{
|
||||
assert getCurrentConnection()==null;
|
||||
assert _handling==false;
|
||||
_handling=true;
|
||||
setCurrentConnection(this);
|
||||
|
||||
while (more_in_buffer && _endp.isOpen())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_request._async.isAsync())
|
||||
{
|
||||
// TODO - handle the case of input being read for a
|
||||
// suspended request.
|
||||
|
||||
Log.debug("async request",_request);
|
||||
if (!_request._async.isComplete())
|
||||
handleRequest();
|
||||
else if (!_parser.isComplete())
|
||||
{
|
||||
int parsed=_parser.parseAvailable();
|
||||
if (parsed>0)
|
||||
progress=true;
|
||||
}
|
||||
|
||||
if (_generator.isCommitted() && !_generator.isComplete())
|
||||
progress|=_generator.flushBuffer()>0;
|
||||
if (_endp.isBufferingOutput())
|
||||
_endp.flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are not ended then parse available
|
||||
if (!_parser.isComplete())
|
||||
{
|
||||
int parsed=_parser.parseAvailable();
|
||||
if (parsed>0)
|
||||
progress=true;
|
||||
}
|
||||
|
||||
// Do we have more generating to do?
|
||||
// Loop here because some writes may take multiple steps and
|
||||
// we need to flush them all before potentially blocking in the
|
||||
// next loop.
|
||||
while (_generator.isCommitted() && !_generator.isComplete())
|
||||
{
|
||||
long written=_generator.flushBuffer();
|
||||
if (written<=0)
|
||||
break;
|
||||
progress=true;
|
||||
if (_endp.isBufferingOutput())
|
||||
_endp.flush();
|
||||
}
|
||||
|
||||
// Flush buffers
|
||||
if (_endp.isBufferingOutput())
|
||||
{
|
||||
_endp.flush();
|
||||
if (!_endp.isBufferingOutput())
|
||||
progress=true;
|
||||
}
|
||||
|
||||
if (!progress)
|
||||
return this;
|
||||
}
|
||||
progress=false;
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
if (Log.isDebugEnabled())
|
||||
{
|
||||
Log.debug("uri="+_uri);
|
||||
Log.debug("fields="+_requestFields);
|
||||
Log.debug(e);
|
||||
}
|
||||
_generator.sendError(e.getStatus(), e.getReason(), null, true);
|
||||
|
||||
_parser.reset(true);
|
||||
_endp.close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
|
||||
// Is this request/response round complete?
|
||||
if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
|
||||
{
|
||||
// look for a switched connection instance?
|
||||
Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||
?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null;
|
||||
|
||||
// have we switched?
|
||||
if (switched!=null)
|
||||
{
|
||||
_parser.reset(true);
|
||||
_generator.reset(true);
|
||||
connection=switched;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No switch, so cleanup and reset
|
||||
if (!_generator.isPersistent() || _endp.isInputShutdown())
|
||||
{
|
||||
_parser.reset(true);
|
||||
more_in_buffer=false;
|
||||
_endp.close();
|
||||
}
|
||||
|
||||
if (more_in_buffer)
|
||||
{
|
||||
reset(false);
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
}
|
||||
else
|
||||
reset(true);
|
||||
progress=true;
|
||||
}
|
||||
}
|
||||
else if (_parser.isIdle() && _endp.isInputShutdown())
|
||||
{
|
||||
more_in_buffer=false;
|
||||
_endp.close();
|
||||
}
|
||||
|
||||
if (_request.isAsyncStarted())
|
||||
{
|
||||
Log.debug("return with suspended request");
|
||||
more_in_buffer=false;
|
||||
}
|
||||
else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint)
|
||||
((AsyncEndPoint)_endp).scheduleWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
setCurrentConnection(null);
|
||||
_handling=false;
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void scheduleTimeout(Timeout.Task task, long timeoutMs)
|
||||
{
|
||||
|
@ -530,7 +377,9 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
/* ------------------------------------------------------------ */
|
||||
public void reset(boolean returnBuffers)
|
||||
{
|
||||
_parser.reset(returnBuffers); // TODO maybe only release when low on resources
|
||||
_parser.reset();
|
||||
if (returnBuffers)
|
||||
_parser.returnBuffers();
|
||||
_requestFields.clear();
|
||||
_request.recycle();
|
||||
|
||||
|
@ -678,6 +527,12 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void commitResponse(boolean last) throws IOException
|
||||
{
|
||||
|
@ -1204,7 +1059,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
Buffer lm = httpContent.getLastModified();
|
||||
long lml=httpContent.getResource().lastModified();
|
||||
if (lm != null)
|
||||
_responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm,lml);
|
||||
_responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm);
|
||||
else if (httpContent.getResource()!=null)
|
||||
{
|
||||
if (lml!=-1)
|
||||
|
|
|
@ -113,7 +113,7 @@ public class LocalConnector extends AbstractConnector
|
|||
};
|
||||
|
||||
endPoint.setGrowOutput(true);
|
||||
HttpConnection connection = new HttpConnection(LocalConnector.this, endPoint, getServer());
|
||||
HttpConnection connection = new BlockingHttpConnection(LocalConnector.this, endPoint, getServer());
|
||||
endPoint.setConnection(connection);
|
||||
connectionOpened(connection);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.bio.SocketEndPoint;
|
||||
import org.eclipse.jetty.server.AbstractConnector;
|
||||
import org.eclipse.jetty.server.BlockingHttpConnection;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -117,7 +118,7 @@ public class SocketConnector extends AbstractConnector
|
|||
*/
|
||||
protected Connection newConnection(EndPoint endpoint)
|
||||
{
|
||||
return new HttpConnection(this, endpoint, getServer());
|
||||
return new BlockingHttpConnection(this, endpoint, getServer());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.eclipse.jetty.io.Connection;
|
|||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.nio.ChannelEndPoint;
|
||||
import org.eclipse.jetty.server.BlockingHttpConnection;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
|
@ -172,7 +173,7 @@ public class BlockingChannelConnector extends AbstractNIOConnector
|
|||
throws IOException
|
||||
{
|
||||
super(channel,BlockingChannelConnector.this._maxIdleTime);
|
||||
_connection = new HttpConnection(BlockingChannelConnector.this,this,getServer());
|
||||
_connection = new BlockingHttpConnection(BlockingChannelConnector.this,this,getServer());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.nio.SelectorManager;
|
||||
import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
|
||||
import org.eclipse.jetty.server.AsyncHttpConnection;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
|
@ -335,7 +336,7 @@ public class SelectChannelConnector extends AbstractNIOConnector
|
|||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
private class SelectChannelHttpConnection extends HttpConnection
|
||||
private class SelectChannelHttpConnection extends AsyncHttpConnection
|
||||
{
|
||||
private final SelectChannelEndPoint _endpoint;
|
||||
|
||||
|
|
|
@ -16,11 +16,13 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpParser;
|
||||
import org.eclipse.jetty.http.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.nio.SelectorManager;
|
||||
import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint;
|
||||
import org.eclipse.jetty.server.AsyncHttpConnection;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -65,7 +67,7 @@ public class SslTruncationAttackTest
|
|||
@Override
|
||||
protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
|
||||
{
|
||||
return new HttpConnection(this, endpoint, server)
|
||||
AsyncHttpConnection connection=new AsyncHttpConnection(this, endpoint, server)
|
||||
{
|
||||
@Override
|
||||
public Connection handle() throws IOException
|
||||
|
@ -74,6 +76,8 @@ public class SslTruncationAttackTest
|
|||
return super.handle();
|
||||
}
|
||||
};
|
||||
((HttpParser)connection.getParser()).setForceContentBuffer(true);
|
||||
return connection;
|
||||
}
|
||||
};
|
||||
server.addConnector(connector);
|
||||
|
@ -151,7 +155,7 @@ public class SslTruncationAttackTest
|
|||
// Sleep for a while to detect eventual spin looping
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
|
||||
Assert.assertEquals("handle() invocations", 1, handleCount.get());
|
||||
Assert.assertTrue("handle() invocations", handleCount.get()<=1);
|
||||
Assert.assertTrue("endpoint not closed", endPointClosed.get());
|
||||
}
|
||||
|
||||
|
|
|
@ -957,7 +957,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
HttpFields fields = r.getHttpFields();
|
||||
|
||||
if (content.getLastModified()!=null)
|
||||
fields.put(HttpHeaders.LAST_MODIFIED_BUFFER,content.getLastModified(),content.getResource().lastModified());
|
||||
fields.put(HttpHeaders.LAST_MODIFIED_BUFFER,content.getLastModified());
|
||||
else if (content.getResource()!=null)
|
||||
{
|
||||
long lml=content.getResource().lastModified();
|
||||
|
|
Loading…
Reference in New Issue