jetty-9 HttpParser tests passing

This commit is contained in:
Greg Wilkins 2012-02-09 23:27:49 +11:00
parent 9c1d3ff0b3
commit 2659d87868
7 changed files with 432 additions and 479 deletions

View File

@ -927,7 +927,7 @@ public class HttpFields
private static final StringMap<Float> __qualities = new StringMap<>(); private static final StringMap<Float> __qualities = new StringMap<>();
static static
{ {
__qualities.put(null, __one); __qualities.put("*", __one);
__qualities.put("1.0", __one); __qualities.put("1.0", __one);
__qualities.put("1", __one); __qualities.put("1", __one);
__qualities.put("0.9", new Float("0.9")); __qualities.put("0.9", new Float("0.9"));
@ -956,14 +956,16 @@ public class HttpFields
if (value.charAt(qe++) == 'q') if (value.charAt(qe++) == 'q')
{ {
qe++; qe++;
Map.Entry<String,Float> entry = __qualities.getEntry(value, qe, value.length() - qe); Float q = __qualities.get(value, qe, value.length() - qe);
if (entry != null) if (q != null)
return (Float) entry.getValue(); return q;
} }
Map<String,String> params = new HashMap<String,String>(4); Map<String,String> params = new HashMap<String,String>(4);
valueParameters(value, params); valueParameters(value, params);
String qs = (String) params.get("q"); String qs = (String) params.get("q");
if (qs==null)
qs="*";
Float q = (Float) __qualities.get(qs); Float q = (Float) __qualities.get(qs);
if (q == null) if (q == null)
{ {

View File

@ -27,28 +27,31 @@ public class HttpParser implements Parser
private static final Logger LOG = Log.getLogger(HttpParser.class); private static final Logger LOG = Log.getLogger(HttpParser.class);
// States // States
public static final int STATE_START=-14; public enum State
public static final int STATE_METHOD=-13; {
public static final int STATE_RESPONSE_VERSION=-12; START,
public static final int STATE_SPACE1=-11; METHOD,
public static final int STATE_STATUS=-10; RESPONSE_VERSION,
public static final int STATE_URI=-9; SPACE1,
public static final int STATE_SPACE2=-8; STATUS,
public static final int STATE_REQUEST_VERSION=-7; URI,
public static final int STATE_REASON=-6; SPACE2,
public static final int STATE_HEADER=-5; REQUEST_VERSION,
public static final int STATE_HEADER_NAME=-4; REASON,
public static final int STATE_HEADER_IN_NAME=-3; HEADER,
public static final int STATE_HEADER_VALUE=-2; HEADER_NAME,
public static final int STATE_HEADER_IN_VALUE=-1; HEADER_IN_NAME,
public static final int STATE_END=0; HEADER_VALUE,
public static final int STATE_EOF_CONTENT=1; HEADER_IN_VALUE,
public static final int STATE_CONTENT=2; END,
public static final int STATE_CHUNKED_CONTENT=3; EOF_CONTENT,
public static final int STATE_CHUNK_SIZE=4; CONTENT,
public static final int STATE_CHUNK_PARAMS=5; CHUNKED_CONTENT,
public static final int STATE_CHUNK=6; CHUNK_SIZE,
public static final int STATE_SEEKING_EOF=7; CHUNK_PARAMS,
CHUNK,
SEEKING_EOF
};
private final EventHandler _handler; private final EventHandler _handler;
private final RequestHandler _requestHandler; private final RequestHandler _requestHandler;
@ -59,7 +62,7 @@ public class HttpParser implements Parser
private boolean _persistent; private boolean _persistent;
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
private int _state=STATE_START; private State _state=State.START;
private String _field0; private String _field0;
private String _field1; private String _field1;
private byte _eol; private byte _eol;
@ -113,7 +116,7 @@ public class HttpParser implements Parser
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
public int getState() public State getState()
{ {
return _state; return _state;
} }
@ -121,13 +124,13 @@ public class HttpParser implements Parser
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
public boolean inContentState() public boolean inContentState()
{ {
return _state > 0; return _state.ordinal() > State.END.ordinal();
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
public boolean inHeaderState() public boolean inHeaderState()
{ {
return _state < 0; return _state.ordinal() < State.END.ordinal();
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
@ -139,17 +142,17 @@ public class HttpParser implements Parser
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public boolean isIdle() public boolean isIdle()
{ {
return isState(STATE_START); return isState(State.START);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public boolean isComplete() public boolean isComplete()
{ {
return isState(STATE_END); return isState(State.END);
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
public boolean isState(int state) public boolean isState(State state)
{ {
return _state == state; return _state == state;
} }
@ -164,49 +167,34 @@ public class HttpParser implements Parser
public void setPersistent(boolean persistent) public void setPersistent(boolean persistent)
{ {
_persistent = persistent; _persistent = persistent;
if (!_persistent &&(_state==STATE_END || _state==STATE_START)) if (!_persistent &&(_state==State.END || _state==State.START))
_state=STATE_SEEKING_EOF; _state=State.SEEKING_EOF;
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**
* Parse until {@link #STATE_END END} state. * Parse until {@link #END END} state.
* If the parser is already in the END state, then it is {@link #reset reset} and re-parsed. * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed.
* @throws IllegalStateException If the buffers have already been partially parsed. * @throws IllegalStateException If the buffers have already been partially parsed.
*/ */
public void parse(ByteBuffer buffer) throws IOException public void parseAll(ByteBuffer buffer) throws IOException
{ {
if (_state==STATE_END) if (_state==State.END)
reset(); reset();
if (_state!=STATE_START) if (_state!=State.START)
throw new IllegalStateException("!START"); throw new IllegalStateException("!START");
// continue parsing // continue parsing
while (_state != STATE_END && buffer.hasRemaining()) while (_state != State.END && buffer.hasRemaining())
if (!parseNext(buffer))
return;
}
/* ------------------------------------------------------------------------------- */
/**
* Parse until END state.
* This method will parse any remaining content in the current buffer. It does not care about the
* {@link #getState current state} of the parser.
* @see #parse
* @see #parseNext
*/
public boolean parseAvailable(ByteBuffer buffer) throws IOException
{
boolean progress=parseNext(buffer);
// continue parsing
while (!isComplete() && buffer.hasRemaining())
{ {
progress |= parseNext(buffer); int remaining=buffer.remaining();
parseNext(buffer);
if (remaining==buffer.remaining())
break;
} }
return progress;
} }
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**
* Parse until next Event. * Parse until next Event.
@ -214,35 +202,28 @@ public class HttpParser implements Parser
*/ */
public boolean parseNext(ByteBuffer buffer) throws IOException public boolean parseNext(ByteBuffer buffer) throws IOException
{ {
int start=-1;
State startState=null;
try try
{ {
int progress=0; if (_state == State.END)
if (_state == STATE_END)
return false; return false;
if (_state == STATE_CONTENT && _contentPosition == _contentLength) if (_state == State.CONTENT && _contentPosition == _contentLength)
{ {
_state=STATE_END; _state=State.END;
_handler.messageComplete(_contentPosition); if(_handler.messageComplete(_contentPosition))
return true; return true;
} }
// Handle header states // Handle header states
byte ch; byte ch;
int last=_state;
int start=-1;
int length=-1; int length=-1;
boolean at_next=false;
while (_state<STATE_END && buffer.hasRemaining()) while (_state.ordinal()<State.END.ordinal() && buffer.hasRemaining() && !at_next)
{ {
if (last!=_state)
{
progress++;
last=_state;
}
ch=buffer.get(); ch=buffer.get();
if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED) if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
@ -254,22 +235,23 @@ public class HttpParser implements Parser
switch (_state) switch (_state)
{ {
case STATE_START: case START:
_contentLength=HttpTokens.UNKNOWN_CONTENT; _contentLength=HttpTokens.UNKNOWN_CONTENT;
_header=null; _header=null;
if (ch > HttpTokens.SPACE || ch<0) if (ch > HttpTokens.SPACE || ch<0)
{ {
start=buffer.position()-1; start=buffer.position()-1;
_state=_requestHandler!=null?STATE_METHOD:STATE_RESPONSE_VERSION; startState=_state;
_state=_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION;
} }
break; break;
case STATE_METHOD: case METHOD:
if (ch == HttpTokens.SPACE) if (ch == HttpTokens.SPACE)
{ {
HttpMethods method=HttpMethods.CACHE.get(buffer,start,buffer.position()-start-1); HttpMethods method=HttpMethods.CACHE.get(buffer,start,buffer.position()-start-1);
_field0=method==null?BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__ISO_8859_1_CHARSET):method.toString(); _field0=method==null?BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__ISO_8859_1_CHARSET):method.toString();
_state=STATE_SPACE1; _state=State.SPACE1;
} }
else if (ch < HttpTokens.SPACE && ch>=0) else if (ch < HttpTokens.SPACE && ch>=0)
{ {
@ -277,15 +259,14 @@ public class HttpParser implements Parser
} }
break; break;
case STATE_RESPONSE_VERSION: case RESPONSE_VERSION:
if (ch == HttpTokens.SPACE) if (ch == HttpTokens.SPACE)
{ {
int l=buffer.position()-start; HttpVersions v=HttpVersions.CACHE.get(buffer,start,buffer.position()-start-1);
HttpVersions v=HttpVersions.CACHE.get(buffer,start,l); _field0=v==null?BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__ISO_8859_1_CHARSET):v.toString();
_field0=v==null?BufferUtil.toString(buffer,start,l,StringUtil.__ISO_8859_1_CHARSET):v.toString();
start=-1; start=-1;
_persistent=HttpVersions.HTTP_1_1==v; _persistent=HttpVersions.HTTP_1_1==v;
_state=STATE_SPACE1; _state=State.SPACE1;
} }
else if (ch < HttpTokens.SPACE && ch>=0) else if (ch < HttpTokens.SPACE && ch>=0)
{ {
@ -293,18 +274,19 @@ public class HttpParser implements Parser
} }
break; break;
case STATE_SPACE1: case SPACE1:
if (ch > HttpTokens.SPACE || ch<0) if (ch > HttpTokens.SPACE || ch<0)
{ {
if (_responseHandler!=null) if (_responseHandler!=null)
{ {
_state=STATE_STATUS; _state=State.STATUS;
_responseStatus=ch-'0'; _responseStatus=ch-'0';
} }
else else
{ {
_state=STATE_URI;
start=buffer.position()-1; start=buffer.position()-1;
startState=_state;
_state=State.URI;
} }
} }
else if (ch < HttpTokens.SPACE) else if (ch < HttpTokens.SPACE)
@ -313,10 +295,10 @@ public class HttpParser implements Parser
} }
break; break;
case STATE_STATUS: case STATUS:
if (ch == HttpTokens.SPACE) if (ch == HttpTokens.SPACE)
{ {
_state=STATE_SPACE2; _state=State.SPACE2;
} }
else if (ch>='0' && ch<='9') else if (ch>='0' && ch<='9')
{ {
@ -324,10 +306,10 @@ public class HttpParser implements Parser
} }
else if (ch < HttpTokens.SPACE && ch>=0) else if (ch < HttpTokens.SPACE && ch>=0)
{ {
_responseHandler.startResponse(_field0, _responseStatus, null); at_next|=_responseHandler.startResponse(_field0, _responseStatus, null);
_eol=ch; _eol=ch;
_state=STATE_HEADER; _state=State.HEADER;
_field0=_field1=null; _field0=_field1=null;
} }
else else
@ -336,84 +318,85 @@ public class HttpParser implements Parser
} }
break; break;
case STATE_URI: case URI:
if (ch == HttpTokens.SPACE) if (ch == HttpTokens.SPACE)
{ {
_field1=BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__UTF8_CHARSET); _field1=BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__UTF8_CHARSET);
start=-1; start=-1;
_state=STATE_SPACE2; _state=State.SPACE2;
} }
else if (ch < HttpTokens.SPACE && ch>=0) else if (ch < HttpTokens.SPACE && ch>=0)
{ {
// HTTP/0.9 // HTTP/0.9
_field1=BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__UTF8_CHARSET); _field1=BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__UTF8_CHARSET);
start=-1; start=-1;
_requestHandler.startRequest(_field0,_field1,null); at_next|=_requestHandler.startRequest(_field0,_field1,null);
_persistent=false; _persistent=false;
_state=STATE_SEEKING_EOF; _state=State.SEEKING_EOF;
_handler.headerComplete(); at_next|=_handler.headerComplete();
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
} }
break; break;
case STATE_SPACE2: case SPACE2:
if (ch > HttpTokens.SPACE || ch<0) if (ch > HttpTokens.SPACE || ch<0)
{ {
_state=_requestHandler!=null?STATE_REQUEST_VERSION:STATE_REASON;
start=buffer.position()-1; start=buffer.position()-1;
startState=_state;
_state=_requestHandler!=null?State.REQUEST_VERSION:State.REASON;
} }
else if (ch < HttpTokens.SPACE) else if (ch < HttpTokens.SPACE)
{ {
if (_responseHandler!=null) if (_responseHandler!=null)
{ {
_responseHandler.startResponse(_field0, _responseStatus, null); at_next|=_responseHandler.startResponse(_field0, _responseStatus, null);
_eol=ch; _eol=ch;
_state=STATE_HEADER; _state=State.HEADER;
_field0=_field1=null; _field0=_field1=null;
} }
else else
{ {
// HTTP/0.9 // HTTP/0.9
_requestHandler.startRequest(_field0, _field1, null); at_next|=_requestHandler.startRequest(_field0, _field1, null);
_persistent=false; _persistent=false;
_state=STATE_SEEKING_EOF; _state=State.SEEKING_EOF;
_handler.headerComplete(); at_next|=_handler.headerComplete();
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
} }
} }
break; break;
case STATE_REQUEST_VERSION: case REQUEST_VERSION:
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{ {
HttpVersions v=HttpVersions.CACHE.get(buffer,start,buffer.position()-start-1); HttpVersions v=HttpVersions.CACHE.get(buffer,start,buffer.position()-start-1);
String version=v==null?BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__ISO_8859_1_CHARSET):v.toString(); String version=v==null?BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__ISO_8859_1_CHARSET):v.toString();
start=-1; start=-1;
_requestHandler.startRequest(_field0, _field1, version); at_next|=_requestHandler.startRequest(_field0, _field1, version);
_eol=ch; _eol=ch;
_persistent=HttpVersions.HTTP_1_1==v; _persistent=HttpVersions.HTTP_1_1==v;
_state=STATE_HEADER; _state=State.HEADER;
_field0=_field1=null; _field0=_field1=null;
continue; continue;
} }
break; break;
case STATE_REASON: case REASON:
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{ {
String reason=BufferUtil.toString(buffer,start,buffer.position()-start,StringUtil.__ISO_8859_1_CHARSET); String reason=BufferUtil.toString(buffer,start,buffer.position()-start-1,StringUtil.__ISO_8859_1_CHARSET);
start=-1; start=-1;
_responseHandler.startResponse(_field0, _responseStatus, reason); at_next|=_responseHandler.startResponse(_field0, _responseStatus, reason);
_eol=ch; _eol=ch;
_state=STATE_HEADER; _state=State.HEADER;
_field0=_field1=null; _field0=_field1=null;
continue; continue;
} }
break; break;
case STATE_HEADER: case HEADER:
switch(ch) switch(ch)
{ {
case HttpTokens.COLON: case HttpTokens.COLON:
@ -422,7 +405,7 @@ public class HttpParser implements Parser
{ {
// header value without name - continuation? // header value without name - continuation?
length=-1; length=-1;
_state=STATE_HEADER_VALUE; _state=State.HEADER_VALUE;
break; break;
} }
@ -497,7 +480,7 @@ public class HttpParser implements Parser
} }
} }
_handler.parsedHeader(_field0, _field1); at_next|=_handler.parsedHeader(_field0, _field1);
} }
_field0=_field1=null; _field0=_field1=null;
_header=null; _header=null;
@ -528,24 +511,24 @@ public class HttpParser implements Parser
switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength) switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
{ {
case HttpTokens.EOF_CONTENT: case HttpTokens.EOF_CONTENT:
_state=STATE_EOF_CONTENT; _state=State.EOF_CONTENT;
_handler.headerComplete(); // May recurse here ! at_next|=_handler.headerComplete(); // May recurse here !
break; break;
case HttpTokens.CHUNKED_CONTENT: case HttpTokens.CHUNKED_CONTENT:
_state=STATE_CHUNKED_CONTENT; _state=State.CHUNKED_CONTENT;
_handler.headerComplete(); // May recurse here ! at_next|=_handler.headerComplete(); // May recurse here !
break; break;
case HttpTokens.NO_CONTENT: case HttpTokens.NO_CONTENT:
_handler.headerComplete(); at_next|=_handler.headerComplete();
_state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?State.END:State.SEEKING_EOF;
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
break; break;
default: default:
_state=STATE_CONTENT; _state=State.CONTENT;
_handler.headerComplete(); // May recurse here ! at_next|=_handler.headerComplete(); // May recurse here !
break; break;
} }
} }
@ -553,15 +536,16 @@ public class HttpParser implements Parser
{ {
// New header // New header
start=buffer.position()-1; start=buffer.position()-1;
startState=_state;
length=1; length=1;
_state=STATE_HEADER_NAME; _state=State.HEADER_NAME;
} }
} }
} }
break; break;
case STATE_HEADER_NAME: case HEADER_NAME:
switch(ch) switch(ch)
{ {
case HttpTokens.CARRIAGE_RETURN: case HttpTokens.CARRIAGE_RETURN:
@ -570,14 +554,14 @@ public class HttpParser implements Parser
_header=HttpHeaders.CACHE.get(buffer,start,length); _header=HttpHeaders.CACHE.get(buffer,start,length);
_field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString(); _field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString();
start=length=-1; start=length=-1;
_state=STATE_HEADER; _state=State.HEADER;
break; break;
case HttpTokens.COLON: case HttpTokens.COLON:
_header=HttpHeaders.CACHE.get(buffer,start,length); _header=HttpHeaders.CACHE.get(buffer,start,length);
_field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString(); _field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString();
start=length=-1; start=length=-1;
_state=STATE_HEADER_VALUE; _state=State.HEADER_VALUE;
break; break;
case HttpTokens.SPACE: case HttpTokens.SPACE:
case HttpTokens.TAB: case HttpTokens.TAB:
@ -585,13 +569,13 @@ public class HttpParser implements Parser
default: default:
{ {
length=buffer.position()-start; length=buffer.position()-start;
_state=STATE_HEADER_IN_NAME; _state=State.HEADER_IN_NAME;
} }
} }
break; break;
case STATE_HEADER_IN_NAME: case HEADER_IN_NAME:
switch(ch) switch(ch)
{ {
case HttpTokens.CARRIAGE_RETURN: case HttpTokens.CARRIAGE_RETURN:
@ -600,25 +584,25 @@ public class HttpParser implements Parser
_header=HttpHeaders.CACHE.get(buffer,start,length); _header=HttpHeaders.CACHE.get(buffer,start,length);
_field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString(); _field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString();
start=length=-1; start=length=-1;
_state=STATE_HEADER; _state=State.HEADER;
break; break;
case HttpTokens.COLON: case HttpTokens.COLON:
_header=HttpHeaders.CACHE.get(buffer,start,length); _header=HttpHeaders.CACHE.get(buffer,start,length);
_field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString(); _field0=_header==null?BufferUtil.toString(buffer,start,length,StringUtil.__ISO_8859_1_CHARSET):_header.toString();
start=length=-1; start=length=-1;
_state=STATE_HEADER_VALUE; _state=State.HEADER_VALUE;
break; break;
case HttpTokens.SPACE: case HttpTokens.SPACE:
case HttpTokens.TAB: case HttpTokens.TAB:
_state=STATE_HEADER_NAME; _state=State.HEADER_NAME;
break; break;
default: default:
length++; length++;
} }
break; break;
case STATE_HEADER_VALUE: case HEADER_VALUE:
switch(ch) switch(ch)
{ {
case HttpTokens.CARRIAGE_RETURN: case HttpTokens.CARRIAGE_RETURN:
@ -643,7 +627,7 @@ public class HttpParser implements Parser
} }
start=length=-1; start=length=-1;
} }
_state=STATE_HEADER; _state=State.HEADER;
break; break;
case HttpTokens.SPACE: case HttpTokens.SPACE:
case HttpTokens.TAB: case HttpTokens.TAB:
@ -651,14 +635,17 @@ public class HttpParser implements Parser
default: default:
{ {
if (start==-1) if (start==-1)
{
start=buffer.position()-1; start=buffer.position()-1;
startState=_state;
}
length=buffer.position()-start; length=buffer.position()-start;
_state=STATE_HEADER_IN_VALUE; _state=State.HEADER_IN_VALUE;
} }
} }
break; break;
case STATE_HEADER_IN_VALUE: case HEADER_IN_VALUE:
switch(ch) switch(ch)
{ {
case HttpTokens.CARRIAGE_RETURN: case HttpTokens.CARRIAGE_RETURN:
@ -683,11 +670,11 @@ public class HttpParser implements Parser
} }
start=length=-1; start=length=-1;
} }
_state=STATE_HEADER; _state=State.HEADER;
break; break;
case HttpTokens.SPACE: case HttpTokens.SPACE:
case HttpTokens.TAB: case HttpTokens.TAB:
_state=STATE_HEADER_VALUE; _state=State.HEADER_VALUE;
break; break;
default: default:
length++; length++;
@ -701,24 +688,17 @@ public class HttpParser implements Parser
// Handle HEAD response // Handle HEAD response
if (_responseStatus>0 && _headResponse) if (_responseStatus>0 && _headResponse)
{ {
_state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF; _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?State.END:State.SEEKING_EOF;
_handler.messageComplete(_contentLength); at_next|=_handler.messageComplete(_contentLength);
} }
// ========================== // ==========================
// Handle _content // Handle _content
last=_state;
ByteBuffer chunk; ByteBuffer chunk;
while (_state > STATE_END && buffer.hasRemaining()) while (_state.ordinal() > State.END.ordinal() && buffer.hasRemaining())
{ {
if (last!=_state)
{
progress++;
last=_state;
}
if (_eol == HttpTokens.CARRIAGE_RETURN && buffer.get(buffer.position()) == HttpTokens.LINE_FEED) if (_eol == HttpTokens.CARRIAGE_RETURN && buffer.get(buffer.position()) == HttpTokens.LINE_FEED)
{ {
_eol=buffer.get(); _eol=buffer.get();
@ -728,20 +708,20 @@ public class HttpParser implements Parser
switch (_state) switch (_state)
{ {
case STATE_EOF_CONTENT: case EOF_CONTENT:
chunk=buffer.asReadOnlyBuffer(); chunk=buffer.asReadOnlyBuffer();
_contentPosition += chunk.remaining(); _contentPosition += chunk.remaining();
buffer.position(buffer.position()+chunk.remaining()); buffer.position(buffer.position()+chunk.remaining());
_handler.content(chunk); // May recurse here at_next|=_handler.content(chunk); // May recurse here
break; break;
case STATE_CONTENT: case CONTENT:
{ {
long remaining=_contentLength - _contentPosition; long remaining=_contentLength - _contentPosition;
if (remaining == 0) if (remaining == 0)
{ {
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?State.END:State.SEEKING_EOF;
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
} }
else else
{ {
@ -757,18 +737,18 @@ public class HttpParser implements Parser
_contentPosition += chunk.remaining(); _contentPosition += chunk.remaining();
buffer.position(buffer.position()+chunk.remaining()); buffer.position(buffer.position()+chunk.remaining());
_handler.content(chunk); // May recurse here at_next|=_handler.content(chunk); // May recurse here
if(_contentPosition == _contentLength) if(_contentPosition == _contentLength)
{ {
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?State.END:State.SEEKING_EOF;
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
} }
} }
break; break;
} }
case STATE_CHUNKED_CONTENT: case CHUNKED_CONTENT:
{ {
ch=buffer.get(buffer.position()); ch=buffer.get(buffer.position());
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
@ -779,12 +759,12 @@ public class HttpParser implements Parser
{ {
_chunkLength=0; _chunkLength=0;
_chunkPosition=0; _chunkPosition=0;
_state=STATE_CHUNK_SIZE; _state=State.CHUNK_SIZE;
} }
break; break;
} }
case STATE_CHUNK_SIZE: case CHUNK_SIZE:
{ {
ch=buffer.get(); ch=buffer.get();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
@ -795,14 +775,14 @@ public class HttpParser implements Parser
{ {
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED) if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
_eol=buffer.get(); _eol=buffer.get();
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?State.END:State.SEEKING_EOF;
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
} }
else else
_state=STATE_CHUNK; _state=State.CHUNK;
} }
else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON) else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
_state=STATE_CHUNK_PARAMS; _state=State.CHUNK_PARAMS;
else if (ch >= '0' && ch <= '9') else if (ch >= '0' && ch <= '9')
_chunkLength=_chunkLength * 16 + (ch - '0'); _chunkLength=_chunkLength * 16 + (ch - '0');
else if (ch >= 'a' && ch <= 'f') else if (ch >= 'a' && ch <= 'f')
@ -814,7 +794,7 @@ public class HttpParser implements Parser
break; break;
} }
case STATE_CHUNK_PARAMS: case CHUNK_PARAMS:
{ {
ch=buffer.get(); ch=buffer.get();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED) if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
@ -824,21 +804,21 @@ public class HttpParser implements Parser
{ {
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED) if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
_eol=buffer.get(); _eol=buffer.get();
_state=_persistent?STATE_END:STATE_SEEKING_EOF; _state=_persistent?State.END:State.SEEKING_EOF;
_handler.messageComplete(_contentPosition); at_next|=_handler.messageComplete(_contentPosition);
} }
else else
_state=STATE_CHUNK; _state=State.CHUNK;
} }
break; break;
} }
case STATE_CHUNK: case CHUNK:
{ {
int remaining=_chunkLength - _chunkPosition; int remaining=_chunkLength - _chunkPosition;
if (remaining == 0) if (remaining == 0)
{ {
_state=STATE_CHUNKED_CONTENT; _state=State.CHUNKED_CONTENT;
} }
else else
{ {
@ -851,13 +831,12 @@ public class HttpParser implements Parser
_contentPosition += remaining; _contentPosition += remaining;
_chunkPosition += remaining; _chunkPosition += remaining;
buffer.position(buffer.position()+remaining); buffer.position(buffer.position()+remaining);
_handler.content(chunk); // May recurse here at_next|=_handler.content(chunk); // May recurse here
_handler.content(chunk);
} }
break;
} }
case STATE_SEEKING_EOF: case SEEKING_EOF:
{ {
buffer.clear().limit(0); buffer.clear().limit(0);
break; break;
@ -866,14 +845,23 @@ public class HttpParser implements Parser
} }
return progress>0; return at_next;
} }
catch(HttpException e) catch(HttpException e)
{ {
_persistent=false; _persistent=false;
_state=STATE_SEEKING_EOF; _state=State.SEEKING_EOF;
throw e; throw e;
} }
finally
{
if (start>=0)
{
buffer.position(start);
_state=startState;
}
}
} }
@ -885,18 +873,18 @@ public class HttpParser implements Parser
// was this unexpected? // was this unexpected?
switch(_state) switch(_state)
{ {
case STATE_END: case END:
case STATE_SEEKING_EOF: case SEEKING_EOF:
_state=STATE_END; _state=State.END;
break; break;
case STATE_EOF_CONTENT: case EOF_CONTENT:
_state=STATE_END; _state=State.END;
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
break; break;
default: default:
_state=STATE_END; _state=State.END;
if (!_headResponse) if (!_headResponse)
_handler.earlyEOF(); _handler.earlyEOF();
_handler.messageComplete(_contentPosition); _handler.messageComplete(_contentPosition);
@ -914,7 +902,7 @@ public class HttpParser implements Parser
public void reset() public void reset()
{ {
// reset state // reset state
_state=_persistent?STATE_START:_state==STATE_END?STATE_END:STATE_SEEKING_EOF; _state=_persistent?State.START:_state==State.END?State.END:State.SEEKING_EOF;
_contentLength=HttpTokens.UNKNOWN_CONTENT; _contentLength=HttpTokens.UNKNOWN_CONTENT;
_contentPosition=0; _contentPosition=0;
_responseStatus=0; _responseStatus=0;
@ -925,7 +913,7 @@ public class HttpParser implements Parser
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
public void setState(int state) public void setState(State state)
{ {
this._state=state; this._state=state;
_contentLength=HttpTokens.UNKNOWN_CONTENT; _contentLength=HttpTokens.UNKNOWN_CONTENT;
@ -946,20 +934,24 @@ public class HttpParser implements Parser
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* Event Handler interface
* These methods return true if they want parsing to return to
* the caller.
*/
public interface EventHandler public interface EventHandler
{ {
public void content(ByteBuffer ref) throws IOException; public boolean content(ByteBuffer ref) throws IOException;
public void headerComplete() throws IOException; public boolean headerComplete() throws IOException;
public void messageComplete(long contentLength) throws IOException; public boolean messageComplete(long contentLength) throws IOException;
/** /**
* This is the method called by parser when a HTTP Header name and value is found * This is the method called by parser when a HTTP Header name and value is found
*/ */
public void parsedHeader(String name, String value) throws IOException; public boolean parsedHeader(String name, String value) throws IOException;
public void earlyEOF(); public boolean earlyEOF();
} }
public interface RequestHandler extends EventHandler public interface RequestHandler extends EventHandler
@ -967,7 +959,7 @@ public class HttpParser implements Parser
/** /**
* This is the method called by parser when the HTTP request line is parsed * This is the method called by parser when the HTTP request line is parsed
*/ */
public abstract void startRequest(String method, String uri, String version) public abstract boolean startRequest(String method, String uri, String version)
throws IOException; throws IOException;
} }
@ -976,7 +968,7 @@ public class HttpParser implements Parser
/** /**
* This is the method called by parser when the HTTP request line is parsed * This is the method called by parser when the HTTP request line is parsed
*/ */
public abstract void startResponse(String version, int status, String reason) public abstract boolean startResponse(String version, int status, String reason)
throws IOException; throws IOException;
} }

View File

@ -25,11 +25,14 @@ public interface Parser
boolean isComplete(); boolean isComplete();
/* ------------------------------------------------------------ */
/** /**
* @return True if progress made * @param buffer
* @return True if parsed to the next unit
* @throws IOException * @throws IOException
*/ */
boolean parseAvailable(ByteBuffer buffer) throws IOException; boolean parseNext(ByteBuffer buffer) throws IOException;
boolean onEOF()throws IOException; boolean onEOF()throws IOException;

View File

@ -56,7 +56,7 @@ import org.eclipse.jetty.util.URIUtil;
* *
* *
*/ */
public class PathMap extends HashMap implements Externalizable public class PathMap<O> extends HashMap<String,O>
{ {
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private static String __pathSpecSeparators = ":,"; private static String __pathSpecSeparators = ":,";
@ -74,13 +74,13 @@ public class PathMap extends HashMap implements Externalizable
} }
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */
final StringMap _prefixMap=new StringMap(); final StringMap<MappedEntry<O>> _prefixMap=new StringMap<>();
final StringMap _suffixMap=new StringMap(); final StringMap<MappedEntry<O>> _suffixMap=new StringMap<>();
final StringMap _exactMap=new StringMap(); final StringMap<MappedEntry<O>> _exactMap=new StringMap<>();
List _defaultSingletonList=null; List _defaultSingletonList=null;
Entry _prefixDefault=null; MappedEntry<O> _prefixDefault=null;
Entry _default=null; MappedEntry<O> _default=null;
final Set _entrySet; final Set _entrySet;
boolean _nodefault=false; boolean _nodefault=false;
@ -121,22 +121,6 @@ public class PathMap extends HashMap implements Externalizable
_entrySet=entrySet(); _entrySet=entrySet();
} }
/* ------------------------------------------------------------ */
public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException
{
HashMap map = new HashMap(this);
out.writeObject(map);
}
/* ------------------------------------------------------------ */
public void readExternal(java.io.ObjectInput in)
throws java.io.IOException, ClassNotFoundException
{
HashMap map = (HashMap)in.readObject();
this.putAll(map);
}
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */
/** Add a single path match to the PathMap. /** Add a single path match to the PathMap.
* @param pathSpec The path specification, or comma separated list of * @param pathSpec The path specification, or comma separated list of
@ -144,19 +128,19 @@ public class PathMap extends HashMap implements Externalizable
* @param object The object the path maps to * @param object The object the path maps to
*/ */
@Override @Override
public Object put(Object pathSpec, Object object) public O put(String pathSpec, O object)
{ {
String str = pathSpec.toString(); String str = pathSpec.toString();
if ("".equals(str.trim())) if ("".equals(str.trim()))
{ {
Entry entry = new Entry("",object); MappedEntry entry = new MappedEntry("",object);
entry.setMapped(""); entry.setMapped("");
_exactMap.put("", entry); _exactMap.put("", entry);
return super.put("", object); return super.put("", object);
} }
StringTokenizer tok = new StringTokenizer(str,__pathSpecSeparators); StringTokenizer tok = new StringTokenizer(str,__pathSpecSeparators);
Object old =null; O old =null;
while (tok.hasMoreTokens()) while (tok.hasMoreTokens())
{ {
@ -168,7 +152,7 @@ public class PathMap extends HashMap implements Externalizable
old = super.put(spec,object); old = super.put(spec,object);
// Make entry that was just created. // Make entry that was just created.
Entry entry = new Entry(spec,object); MappedEntry entry = new MappedEntry(spec,object);
if (entry.getKey().equals(spec)) if (entry.getKey().equals(spec))
{ {
@ -225,9 +209,9 @@ public class PathMap extends HashMap implements Externalizable
* @param path the path. * @param path the path.
* @return Map.Entry of the best matched or null. * @return Map.Entry of the best matched or null.
*/ */
public Entry getMatch(String path) public MappedEntry<O> getMatch(String path)
{ {
Map.Entry entry=null; MappedEntry<O> entry=null;
if (path==null) if (path==null)
return null; return null;
@ -237,23 +221,23 @@ public class PathMap extends HashMap implements Externalizable
//special case //special case
if (l == 1 && path.charAt(0)=='/') if (l == 1 && path.charAt(0)=='/')
{ {
entry = (Map.Entry)_exactMap.get(""); entry = _exactMap.get("");
if (entry != null) if (entry != null)
return (Entry)entry; return (MappedEntry)entry;
} }
// try exact match // try exact match
entry=_exactMap.getEntry(path,0,l); entry=_exactMap.get(path,0,l);
if (entry!=null) if (entry!=null)
return (Entry) entry.getValue(); return entry;
// prefix search // prefix search
int i=l; int i=l;
while((i=path.lastIndexOf('/',i-1))>=0) while((i=path.lastIndexOf('/',i-1))>=0)
{ {
entry=_prefixMap.getEntry(path,0,i); entry=_prefixMap.get(path,0,i);
if (entry!=null) if (entry!=null)
return (Entry) entry.getValue(); return entry;
} }
// Prefix Default // Prefix Default
@ -264,9 +248,9 @@ public class PathMap extends HashMap implements Externalizable
i=0; i=0;
while ((i=path.indexOf('.',i+1))>0) while ((i=path.indexOf('.',i+1))>0)
{ {
entry=_suffixMap.getEntry(path,i+1,l-i-1); entry=_suffixMap.get(path,i+1,l-i-1);
if (entry!=null) if (entry!=null)
return (Entry) entry.getValue(); return entry;
} }
// Default // Default
@ -281,7 +265,7 @@ public class PathMap extends HashMap implements Externalizable
*/ */
public Object getLazyMatches(String path) public Object getLazyMatches(String path)
{ {
Map.Entry entry; MappedEntry entry;
Object entries=null; Object entries=null;
if (path==null) if (path==null)
@ -290,17 +274,17 @@ public class PathMap extends HashMap implements Externalizable
int l=path.length(); int l=path.length();
// try exact match // try exact match
entry=_exactMap.getEntry(path,0,l); entry=_exactMap.get(path,0,l);
if (entry!=null) if (entry!=null)
entries=LazyList.add(entries,entry.getValue()); entries=LazyList.add(entries,entry);
// prefix search // prefix search
int i=l-1; int i=l-1;
while((i=path.lastIndexOf('/',i-1))>=0) while((i=path.lastIndexOf('/',i-1))>=0)
{ {
entry=_prefixMap.getEntry(path,0,i); entry=_prefixMap.get(path,0,i);
if (entry!=null) if (entry!=null)
entries=LazyList.add(entries,entry.getValue()); entries=LazyList.add(entries,entry);
} }
// Prefix Default // Prefix Default
@ -311,9 +295,9 @@ public class PathMap extends HashMap implements Externalizable
i=0; i=0;
while ((i=path.indexOf('.',i+1))>0) while ((i=path.indexOf('.',i+1))>0)
{ {
entry=_suffixMap.getEntry(path,i+1,l-i-1); entry=_suffixMap.get(path,i+1,l-i-1);
if (entry!=null) if (entry!=null)
entries=LazyList.add(entries,entry.getValue()); entries=LazyList.add(entries,entry);
} }
// Default // Default
@ -348,13 +332,13 @@ public class PathMap extends HashMap implements Externalizable
*/ */
public boolean containsMatch(String path) public boolean containsMatch(String path)
{ {
Entry match = getMatch(path); MappedEntry match = getMatch(path);
return match!=null && !match.equals(_default); return match!=null && !match.equals(_default);
} }
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */
@Override @Override
public Object remove(Object pathSpec) public O remove(Object pathSpec)
{ {
if (pathSpec!=null) if (pathSpec!=null)
{ {
@ -532,30 +516,29 @@ public class PathMap extends HashMap implements Externalizable
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public static class Entry implements Map.Entry public static class MappedEntry<O> implements Map.Entry<String,O>
{ {
private final Object key; private final String key;
private final Object value; private final O value;
private String mapped; private String mapped;
private transient String string;
Entry(Object key, Object value) MappedEntry(String key, O value)
{ {
this.key=key; this.key=key;
this.value=value; this.value=value;
} }
public Object getKey() public String getKey()
{ {
return key; return key;
} }
public Object getValue() public O getValue()
{ {
return value; return value;
} }
public Object setValue(Object o) public O setValue(O o)
{ {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -563,9 +546,7 @@ public class PathMap extends HashMap implements Externalizable
@Override @Override
public String toString() public String toString()
{ {
if (string==null) return key+"="+value;
string=key+"="+value;
return string;
} }
public String getMapped() public String getMapped()

View File

@ -37,7 +37,7 @@ public class HttpParserTest
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("POST", f0); assertEquals("POST", f0);
assertEquals("/foo", f1); assertEquals("/foo", f1);
assertEquals("HTTP/1.0", f2); assertEquals("HTTP/1.0", f2);
@ -52,7 +52,7 @@ public class HttpParserTest
f2= null; f2= null;
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("GET", f0); assertEquals("GET", f0);
assertEquals("/999", f1); assertEquals("/999", f1);
assertEquals(null, f2); assertEquals(null, f2);
@ -67,7 +67,7 @@ public class HttpParserTest
f2= null; f2= null;
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("POST", f0); assertEquals("POST", f0);
assertEquals("/222", f1); assertEquals("/222", f1);
assertEquals(null, f2); assertEquals(null, f2);
@ -81,7 +81,7 @@ public class HttpParserTest
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("POST", f0); assertEquals("POST", f0);
assertEquals("/fo\u0690", f1); assertEquals("/fo\u0690", f1);
assertEquals("HTTP/1.0", f2); assertEquals("HTTP/1.0", f2);
@ -95,7 +95,7 @@ public class HttpParserTest
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("POST", f0); assertEquals("POST", f0);
assertEquals("/foo?param=\u0690", f1); assertEquals("/foo?param=\u0690", f1);
assertEquals("HTTP/1.0", f2); assertEquals("HTTP/1.0", f2);
@ -108,7 +108,7 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012"); ByteBuffer buffer= BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012");
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertTrue(handler.request); assertTrue(handler.request);
assertEquals("CONNECT", f0); assertEquals("CONNECT", f0);
assertEquals("192.168.1.2:80", f1); assertEquals("192.168.1.2:80", f1);
@ -120,19 +120,19 @@ public class HttpParserTest
public void testHeaderParse() throws Exception public void testHeaderParse() throws Exception
{ {
ByteBuffer buffer= BufferUtil.toBuffer( ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" "GET / HTTP/1.0\015\012" +
+ "Host: localhost\015\012" "Host: localhost\015\012" +
+ "Header1: value1\015\012" "Header1: value1\015\012" +
+ "Header2 : value 2a \015\012" "Header2 : value 2a \015\012" +
+ " value 2b \015\012" " value 2b \015\012" +
+ "Header3: \015\012" "Header3: \015\012" +
+ "Header4 \015\012" "Header4 \015\012" +
+ " value4\015\012" " value4\015\012" +
+ "Server5: notServer\015\012" "Server5: notServer\015\012" +
+ "\015\012"); "\015\012");
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("GET", f0); assertEquals("GET", f0);
assertEquals("/", f1); assertEquals("/", f1);
@ -151,14 +151,58 @@ public class HttpParserTest
assertEquals("notServer", val[5]); assertEquals("notServer", val[5]);
assertEquals(5, h); assertEquals(5, h);
} }
@Test @Test
public void testHttpHeaders() throws Exception public void testSplitHeaderParse() throws Exception
{ {
ByteBuffer buffer= BufferUtil.toBuffer("Transfer-Encoding"); ByteBuffer buffer= BufferUtil.toBuffer(
assertEquals(HttpHeaders.TRANSFER_ENCODING,HttpHeaders.CACHE.get(buffer)); "XXXXGET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
"Header2 : value 2a \015\012" +
" value 2b \015\012" +
"Header3: \015\012" +
"Header4 \015\012" +
" value4\015\012" +
"Server5: notServer\015\012" +
"\015\012ZZZZ");
buffer.position(2);
buffer.limit(buffer.capacity()-2);
buffer=buffer.slice();
for (int i=0;i<buffer.capacity()-4;i++)
{
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
buffer.position(2);
buffer.limit(2+i);
if (!parser.parseNext(buffer))
{
buffer.limit(buffer.capacity()-2);
parser.parseNext(buffer);
}
assertEquals("GET", f0);
assertEquals("/", f1);
assertEquals("HTTP/1.0", f2);
assertEquals("Host", hdr[0]);
assertEquals("localhost", val[0]);
assertEquals("Header1", hdr[1]);
assertEquals("value1", val[1]);
assertEquals("Header2", hdr[2]);
assertEquals("value 2a value 2b", val[2]);
assertEquals("Header3", hdr[3]);
assertEquals(null, val[3]);
assertEquals("Header4", hdr[4]);
assertEquals("value4", val[4]);
assertEquals("Server5", hdr[5]);
assertEquals("notServer", val[5]);
assertEquals(5, h);
}
} }
@Test @Test
public void testChunkParse() throws Exception public void testChunkParse() throws Exception
@ -175,7 +219,7 @@ public class HttpParserTest
+ "0\015\012"); + "0\015\012");
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(buffer); parser.parseAll(buffer);
assertEquals("GET", f0); assertEquals("GET", f0);
assertEquals("/chunk", f1); assertEquals("/chunk", f1);
@ -189,9 +233,8 @@ public class HttpParserTest
@Test @Test
public void testMultiParse() throws Exception public void testMultiParse() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput( "GET /mp HTTP/1.0\015\012"
"GET /mp HTTP/1.0\015\012"
+ "Connection: Keep-Alive\015\012" + "Connection: Keep-Alive\015\012"
+ "Header1: value1\015\012" + "Header1: value1\015\012"
+ "Transfer-Encoding: chunked\015\012" + "Transfer-Encoding: chunked\015\012"
@ -213,13 +256,10 @@ public class HttpParserTest
+ "\015\012" + "\015\012"
+ "0123456789\015\012"); + "0123456789\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
ByteBuffer content=BufferUtil.allocate(8192);
SimpleBuffers buffers=new SimpleBuffers(buffer,content);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("GET", f0); assertEquals("GET", f0);
assertEquals("/mp", f1); assertEquals("/mp", f1);
assertEquals("HTTP/1.0", f2); assertEquals("HTTP/1.0", f2);
@ -227,8 +267,9 @@ public class HttpParserTest
assertEquals("Header1", hdr[1]); assertEquals("Header1", hdr[1]);
assertEquals("value1", val[1]); assertEquals("value1", val[1]);
assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content); assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
parser.parse(); parser.reset();
parser.parseNext(buffer);
assertEquals("POST", f0); assertEquals("POST", f0);
assertEquals("/foo", f1); assertEquals("/foo", f1);
assertEquals("HTTP/1.0", f2); assertEquals("HTTP/1.0", f2);
@ -237,7 +278,9 @@ public class HttpParserTest
assertEquals("value2", val[1]); assertEquals("value2", val[1]);
assertEquals(null, _content); assertEquals(null, _content);
parser.parse(); parser.reset();
parser.parseNext(buffer);
parser.onEOF();
assertEquals("PUT", f0); assertEquals("PUT", f0);
assertEquals("/doodle", f1); assertEquals("/doodle", f1);
assertEquals("HTTP/1.0", f2); assertEquals("HTTP/1.0", f2);
@ -247,117 +290,19 @@ public class HttpParserTest
assertEquals("0123456789", _content); assertEquals("0123456789", _content);
} }
@Test
public void testStreamParse() throws Exception
{
StringEndPoint io=new StringEndPoint();
String http="GET / HTTP/1.1\015\012"
+ "Host: test\015\012"
+ "Header1: value1\015\012"
+ "Transfer-Encoding: chunked\015\012"
+ "\015\012"
+ "a;\015\012"
+ "0123456789\015\012"
+ "1a\015\012"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
+ "0\015\012"
+ "POST /foo HTTP/1.1\015\012"
+ "Host: test\015\012"
+ "Header2: value2\015\012"
+ "Content-Length: 0\015\012"
+ "\015\012"
+ "PUT /doodle HTTP/1.1\015\012"
+ "Host: test\015\012"
+ "Connection: close\015\012"
+ "Header3: value3\015\012"
+ "Content-Length: 10\015\012"
+ "\015\012"
+ "0123456789\015\012";
int[] tests=
{
1024,
http.length() + 3,
http.length() + 2,
http.length() + 1,
http.length() + 0,
http.length() - 1,
http.length() - 2,
http.length() / 2,
http.length() / 3,
128,
32
};
for (int t= 0; t < tests.length; t++)
{
String tst="t"+t+"="+tests[t];
try
{
f0=f1=f2=null;
h=0;
ByteBuffer buffer= BufferUtil.allocate(tests[t]);
ByteBuffer content=BufferUtil.allocate(8192);
SimpleBuffers buffers=new SimpleBuffers(buffer,content);
Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler);
io.setInput(http);
// System.err.println(tst);
parser.parse();
assertEquals(tst,"GET", f0);
assertEquals(tst,"/", f1);
assertEquals(tst,"HTTP/1.1", f2);
assertEquals(tst,2, h);
assertEquals(tst,"Header1", hdr[1]);
assertEquals(tst,"value1", val[1]);
assertEquals(tst,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
parser.parse();
assertEquals(tst,"POST", f0);
assertEquals(tst,"/foo", f1);
assertEquals(tst,"HTTP/1.1", f2);
assertEquals(tst,2, h);
assertEquals(tst,"Header2", hdr[1]);
assertEquals(tst,"value2", val[1]);
assertEquals(tst,null, _content);
parser.parse();
assertEquals(tst,"PUT", f0);
assertEquals(tst,"/doodle", f1);
assertEquals(tst,"HTTP/1.1", f2);
assertEquals(tst,3, h);
assertEquals(tst,"Header3", hdr[2]);
assertEquals(tst,"value3", val[2]);
assertEquals(tst,"0123456789", _content);
}
catch(Exception e)
{
if (t+1 < tests.length)
throw e;
assertTrue(e.toString().indexOf("FULL")>=0);
}
}
}
@Test @Test
public void testResponseParse0() throws Exception public void testResponseParse0() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 200 Correct\015\012" "HTTP/1.1 200 Correct\015\012"
+ "Content-Length: 10\015\012" + "Content-Length: 10\015\012"
+ "Content-Type: text/plain\015\012" + "Content-Type: text/plain\015\012"
+ "\015\012" + "\015\012"
+ "0123456789\015\012"); + "0123456789\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("200", f1); assertEquals("200", f1);
assertEquals("Correct", f2); assertEquals("Correct", f2);
@ -369,17 +314,14 @@ public class HttpParserTest
@Test @Test
public void testResponseParse1() throws Exception public void testResponseParse1() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 304 Not-Modified\015\012" "HTTP/1.1 304 Not-Modified\015\012"
+ "Connection: close\015\012" + "Connection: close\015\012"
+ "\015\012"); + "\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("304", f1); assertEquals("304", f1);
assertEquals("Not-Modified", f2); assertEquals("Not-Modified", f2);
@ -390,8 +332,7 @@ public class HttpParserTest
@Test @Test
public void testResponseParse2() throws Exception public void testResponseParse2() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 204 No-Content\015\012" "HTTP/1.1 204 No-Content\015\012"
+ "Header: value\015\012" + "Header: value\015\012"
+ "\015\012" + "\015\012"
@ -400,19 +341,21 @@ public class HttpParserTest
+ "Content-Type: text/plain\015\012" + "Content-Type: text/plain\015\012"
+ "\015\012" + "\015\012"
+ "0123456789\015\012"); + "0123456789\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("204", f1); assertEquals("204", f1);
assertEquals("No-Content", f2); assertEquals("No-Content", f2);
assertTrue(headerCompleted); assertTrue(headerCompleted);
assertTrue(messageCompleted); assertTrue(messageCompleted);
parser.parse();
parser.setPersistent(true);
parser.reset();
parser.parseNext(buffer);
parser.onEOF();
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("200", f1); assertEquals("200", f1);
assertEquals("Correct", f2); assertEquals("Correct", f2);
@ -425,19 +368,16 @@ public class HttpParserTest
@Test @Test
public void testResponseParse3() throws Exception public void testResponseParse3() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 200\015\012" "HTTP/1.1 200\015\012"
+ "Content-Length: 10\015\012" + "Content-Length: 10\015\012"
+ "Content-Type: text/plain\015\012" + "Content-Type: text/plain\015\012"
+ "\015\012" + "\015\012"
+ "0123456789\015\012"); + "0123456789\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("200", f1); assertEquals("200", f1);
assertEquals(null, f2); assertEquals(null, f2);
@ -449,19 +389,16 @@ public class HttpParserTest
@Test @Test
public void testResponseParse4() throws Exception public void testResponseParse4() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 200 \015\012" "HTTP/1.1 200 \015\012"
+ "Content-Length: 10\015\012" + "Content-Length: 10\015\012"
+ "Content-Type: text/plain\015\012" + "Content-Type: text/plain\015\012"
+ "\015\012" + "\015\012"
+ "0123456789\015\012"); + "0123456789\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("200", f1); assertEquals("200", f1);
assertEquals(null, f2); assertEquals(null, f2);
@ -473,17 +410,14 @@ public class HttpParserTest
@Test @Test
public void testResponse304WithContentLength() throws Exception public void testResponse304WithContentLength() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 304 found\015\012" "HTTP/1.1 304 found\015\012"
+ "Content-Length: 10\015\012" + "Content-Length: 10\015\012"
+ "\015\012"); + "\015\012");
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("304", f1); assertEquals("304", f1);
assertEquals("found", f2); assertEquals("found", f2);
@ -495,8 +429,7 @@ public class HttpParserTest
@Test @Test
public void testSeekEOF() throws Exception public void testSeekEOF() throws Exception
{ {
StringEndPoint io=new StringEndPoint(); ByteBuffer buffer= BufferUtil.toBuffer(
io.setInput(
"HTTP/1.1 200 OK\015\012" "HTTP/1.1 200 OK\015\012"
+ "Content-Length: 0\015\012" + "Content-Length: 0\015\012"
+ "Connection: close\015\012" + "Connection: close\015\012"
@ -504,14 +437,11 @@ public class HttpParserTest
+ "\015\012" // extra CRLF ignored + "\015\012" // extra CRLF ignored
+ "HTTP/1.1 400 OK\015\012"); // extra data causes close + "HTTP/1.1 400 OK\015\012"); // extra data causes close
ByteBuffer buffer= BufferUtil.allocate(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler); HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parse(); parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0); assertEquals("HTTP/1.1", f0);
assertEquals("200", f1); assertEquals("200", f1);
assertEquals("OK", f2); assertEquals("OK", f2);
@ -538,15 +468,19 @@ public class HttpParserTest
private HttpFields fields; private HttpFields fields;
private boolean request; private boolean request;
public void content(ByteBuffer ref) public boolean content(ByteBuffer ref)
{ {
if (_content==null) if (_content==null)
_content=""; _content="";
_content= _content + BufferUtil.toString(ref,StringUtil.__UTF8_CHARSET); String c = BufferUtil.toString(ref,StringUtil.__UTF8_CHARSET);
//System.err.println("content '"+c+"'");
_content= _content + c;
return false;
} }
public void startRequest(String tok0, String tok1, String tok2) public boolean startRequest(String tok0, String tok1, String tok2)
{ {
//System.err.println("request "+tok0+" "+tok1+" "+tok2);
request=true; request=true;
h= -1; h= -1;
hdr= new String[9]; hdr= new String[9];
@ -558,16 +492,20 @@ public class HttpParserTest
fields=new HttpFields(); fields=new HttpFields();
messageCompleted = false; messageCompleted = false;
headerCompleted = false; headerCompleted = false;
return false;
} }
public void parsedHeader(String name, String value) public boolean parsedHeader(String name, String value)
{ {
//System.err.println("header "+name+": "+value);
hdr[++h]= name; hdr[++h]= name;
val[h]= value; val[h]= value;
return false;
} }
public void headerComplete() public boolean headerComplete()
{ {
//System.err.println("headerComplete");
_content= null; _content= null;
String s0=fields.toString(); String s0=fields.toString();
String s1=fields.toString(); String s1=fields.toString();
@ -579,14 +517,17 @@ public class HttpParserTest
} }
headerCompleted = true; headerCompleted = true;
return false;
} }
public void messageComplete(long contentLength) public boolean messageComplete(long contentLength)
{ {
//System.err.println("messageComplete");
messageCompleted = true; messageCompleted = true;
return true;
} }
public void startResponse(String version, int status, String reason) public boolean startResponse(String version, int status, String reason)
{ {
request=false; request=false;
f0 = version.toString(); f0 = version.toString();
@ -599,11 +540,13 @@ public class HttpParserTest
messageCompleted = false; messageCompleted = false;
headerCompleted = false; headerCompleted = false;
return false;
} }
@Override @Override
public void earlyEOF() public boolean earlyEOF()
{ {
return true;
} }
} }
} }

View File

@ -16,6 +16,8 @@ package org.eclipse.jetty.util;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import javax.swing.text.Position;
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**
@ -179,11 +181,11 @@ public class BufferUtil
byte[] array = buffer.hasArray()?buffer.array():null; byte[] array = buffer.hasArray()?buffer.array():null;
if (array == null) if (array == null)
{ {
ByteBuffer slice=buffer.slice(); ByteBuffer ro=buffer.asReadOnlyBuffer();
slice.position(position); ro.position(position);
slice.limit(position+length); ro.limit(position+length);
byte[] to = new byte[length]; byte[] to = new byte[length];
slice.get(to); ro.get(to);
return new String(to,0,to.length,charset); return new String(to,0,to.length,charset);
} }
return new String(array,buffer.arrayOffset()+position,length,charset); return new String(array,buffer.arrayOffset()+position,length,charset);
@ -461,6 +463,73 @@ public class BufferUtil
return ByteBuffer.wrap(s.getBytes(charset)); return ByteBuffer.wrap(s.getBytes(charset));
} }
public static String toDetailString(ByteBuffer buffer)
{
StringBuilder buf = new StringBuilder();
buf.append("[p=");
buf.append(buffer.position());
buf.append(",l=");
buf.append(buffer.limit());
buf.append(",c=");
buf.append(buffer.capacity());
buf.append("]={");
for (int i=0;i<buffer.position();i++)
{
char c=(char)buffer.get(i);
if (c>=' ')
buf.append(c);
else if (c=='\r'||c=='\n')
buf.append('|');
else
buf.append('?');
if (i==16&&buffer.position()>32)
{
buf.append("...");
i=buffer.position()-16;
}
}
buf.append("}{");
for (int i=buffer.position();i<buffer.limit();i++)
{
char c=(char)buffer.get(i);
if (c>=' ')
buf.append(c);
else if (c=='\r'||c=='\n')
buf.append('|');
else
buf.append('?');
if (i==buffer.position()+16&&buffer.limit()>buffer.position()+32)
{
buf.append("...");
i=buffer.limit()-16;
}
}
buf.append("}{");
int limit=buffer.limit();
buffer.limit(buffer.capacity());
for (int i=limit;i<buffer.capacity();i++)
{
char c=(char)buffer.get(i);
if (c>=' ')
buf.append(c);
else if (c=='\r'||c=='\n')
buf.append('|');
else
buf.append('?');
if (i==limit+16&&buffer.capacity()>limit+32)
{
buf.append("...");
i=buffer.capacity()-16;
}
}
buffer.limit(limit);
buf.append("}");
return buf.toString();
}
private final static int[] decDivisors = private final static int[] decDivisors =
{ 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 }; { 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 };

View File

@ -111,44 +111,6 @@ public class StringMap<O> extends AbstractMap<String,O>
return _caseInsensitive; return _caseInsensitive;
} }
/* ------------------------------------------------------------ */
public static void main(String[] arg)
{
StringMap<String> map = new StringMap<>();
System.err.println("null="+map.get("nothing"));
map.put("foo","1");
System.err.println("null="+map.get("nothing"));
System.err.println("null="+map.get("foobar"));
System.err.println("1="+map.get("foo"));
System.err.println("null="+map.get("fo"));
map.put("foobar","2");
System.err.println("null="+map.get("nothing"));
System.err.println("2="+map.get("foobar"));
System.err.println("1="+map.get("foo"));
System.err.println("null="+map.get("fo"));
System.err.println("null="+map.get("foob"));
map.put("foob","3");
System.err.println("null="+map.get("nothing"));
System.err.println("2="+map.get("foobar"));
System.err.println("3="+map.get("foob"));
System.err.println("1="+map.get("foo"));
map.put("fool","4");
map.put("fop","5");
map.put("fred","6");
System.err.println("2="+map.get(BufferUtil.toBuffer("foobar")));
System.err.println("3="+map.get(BufferUtil.toBuffer("foob")));
System.err.println("1="+map.get(BufferUtil.toBuffer("foo")));
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
public O put(String key, O value) public O put(String key, O value)
@ -178,10 +140,11 @@ public class StringMap<O> extends AbstractMap<String,O>
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public O get(ByteBuffer buffer, int position, int length) public O get(ByteBuffer buffer, int position, int length)
{ {
ByteBuffer slice=buffer.slice(); ByteBuffer ro=buffer.asReadOnlyBuffer();
slice.position(position); ro.limit(ro.capacity());
slice.limit(position+length); ro.position(position);
return _map.get(slice); ro.limit(position+length);
return _map.get(ro);
} }