{
// handler last header if any
if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
{
Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
_cached=null;
Buffer value=_multiLineValue == null ? (Buffer) _tok1 : (Buffer) new ByteArrayBuffer(_multiLineValue);
int ho=HttpHeaders.CACHE.getOrdinal(header);
if (ho >= 0)
{
int vo=-1;
switch (ho)
{
case HttpHeaders.CONTENT_LENGTH_ORDINAL:
if (_contentLength != HttpTokens.CHUNKED_CONTENT)
{
_contentLength=BufferUtil.toLong(value);
if (_contentLength <= 0)
_contentLength=HttpTokens.NO_CONTENT;
}
break;
case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
value=HttpHeaderValues.CACHE.lookup(value);
vo=HttpHeaderValues.CACHE.getOrdinal(value);
if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
_contentLength=HttpTokens.CHUNKED_CONTENT;
else
{
String c=value.toString();
if (c.endsWith(HttpHeaderValues.CHUNKED))
_contentLength=HttpTokens.CHUNKED_CONTENT;
else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
throw new HttpException(400,null);
}
break;
}
}
_handler.parsedHeader(header, value);
_tok0.setPutIndex(_tok0.getIndex());
_tok1.setPutIndex(_tok1.getIndex());
_multiLineValue=null;
}
// 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)
{
if (_responseStatus == 0 // request
|| _responseStatus == 304 // not-modified response
|| _responseStatus == 204 // no-content response
|| _responseStatus < 200) // 1xx response
_contentLength=HttpTokens.NO_CONTENT;
else
_contentLength=HttpTokens.EOF_CONTENT;
}
_contentPosition=0;
_eol=ch;
// We convert _contentLength to an int for this switch statement because
// we don't care about the amount of data available just whether there is some.
switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
{
case HttpTokens.EOF_CONTENT:
_state=STATE_EOF_CONTENT;
if(_body==null && _buffers!=null)
_body=_buffers.getBuffer(_contentBufferSize);
_handler.headerComplete(); // May recurse here !
break;
case HttpTokens.CHUNKED_CONTENT:
_state=STATE_CHUNKED_CONTENT;
if (_body==null && _buffers!=null)
_body=_buffers.getBuffer(_contentBufferSize);
_handler.headerComplete(); // May recurse here !
break;
case HttpTokens.NO_CONTENT:
_state=STATE_END;
_handler.headerComplete();
_handler.messageComplete(_contentPosition);
break;
default:
_state=STATE_CONTENT;
if(_forceContentBuffer ||
(_buffers!=null && _body==null && _buffer==_header && _contentLength>=(_header.capacity()-_header.getIndex())))
_body=_buffers.getBuffer(_contentBufferSize);
_handler.headerComplete(); // May recurse here !
break;
}
return total_filled;
}
else
{
// New header
_length=1;
_buffer.mark();
_state=STATE_HEADER_NAME;
// try cached name!
if (array!=null)
{
_cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
if (_cached!=null)
{
_length=_cached.length();
_buffer.setGetIndex(_buffer.markIndex()+_length);
length=_buffer.length();
}
}
}
}
break;
case STATE_HEADER_NAME:
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{
if (_length > 0)
_tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
_eol=ch;
_state=STATE_HEADER;
}
else if (ch == HttpTokens.COLON)
{
if (_length > 0 && _cached==null)
_tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
_length=-1;
_state=STATE_HEADER_VALUE;
}
else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB)
{
// Drag the mark
_cached=null;
if (_length == -1) _buffer.mark();
_length=_buffer.getIndex() - _buffer.markIndex();
}
break;
case STATE_HEADER_VALUE:
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{
if (_length > 0)
{
if (_tok1.length() == 0)
_tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
else
{
// Continuation line!
if (_multiLineValue == null) _multiLineValue=_tok1.toString();
_tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
_multiLineValue += " " + _tok1.toString();
}
}
_eol=ch;
_state=STATE_HEADER;
}
else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB)
{
if (_length == -1) _buffer.mark();
_length=_buffer.getIndex() - _buffer.markIndex();
}
break;
}
} // end of HEADER states loop
// ==========================
// Handle _content
length=_buffer.length();
if (_input!=null)
_input._contentView=_contentView;
Buffer chunk;
while (_state > STATE_END && length > 0)
{
if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
{
_eol=_buffer.get();
length=_buffer.length();
continue;
}
_eol=0;
switch (_state)
{
case STATE_EOF_CONTENT:
chunk=_buffer.get(_buffer.length());
_contentPosition += chunk.length();
_contentView.update(chunk);
_handler.content(chunk); // May recurse here
// TODO adjust the _buffer to keep unconsumed content
return total_filled;
case STATE_CONTENT:
{
long remaining=_contentLength - _contentPosition;
if (remaining == 0)
{
_state=STATE_END;
_handler.messageComplete(_contentPosition);
return total_filled;
}
if (length > remaining)
{
// We can cast reamining to an int as we know that it is smaller than
// or equal to length which is already an int.
length=(int)remaining;
}
chunk=_buffer.get(length);
_contentPosition += chunk.length();
_contentView.update(chunk);
_handler.content(chunk); // May recurse here
// TODO adjust the _buffer to keep unconsumed content
return total_filled;
}
case STATE_CHUNKED_CONTENT:
{
ch=_buffer.peek();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
_eol=_buffer.get();
else if (ch <= HttpTokens.SPACE)
_buffer.get();
else
{
_chunkLength=0;
_chunkPosition=0;
_state=STATE_CHUNK_SIZE;
}
break;
}
case STATE_CHUNK_SIZE:
{
ch=_buffer.get();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{
_eol=ch;
if (_chunkLength == 0)
{
_state=STATE_END;
_handler.messageComplete(_contentPosition);
return total_filled;
}
else
_state=STATE_CHUNK;
}
else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
_state=STATE_CHUNK_PARAMS;
else if (ch >= '0' && ch <= '9')
_chunkLength=_chunkLength * 16 + (ch - '0');
else if (ch >= 'a' && ch <= 'f')
_chunkLength=_chunkLength * 16 + (10 + ch - 'a');
else if (ch >= 'A' && ch <= 'F')
_chunkLength=_chunkLength * 16 + (10 + ch - 'A');
else
throw new IOException("bad chunk char: " + ch);
break;
}
case STATE_CHUNK_PARAMS:
{
ch=_buffer.get();
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{
_eol=ch;
if (_chunkLength == 0)
{
_state=STATE_END;
_handler.messageComplete(_contentPosition);
return total_filled;
}
else
_state=STATE_CHUNK;
}
break;
}
case STATE_CHUNK:
{
int remaining=_chunkLength - _chunkPosition;
if (remaining == 0)
{
_state=STATE_CHUNKED_CONTENT;
break;
}
else if (length > remaining)
length=remaining;
chunk=_buffer.get(length);
_contentPosition += chunk.length();
_chunkPosition += chunk.length();
_contentView.update(chunk);
_handler.content(chunk); // May recurse here
// TODO adjust the _buffer to keep unconsumed content
return total_filled;
}