jetty-9 improved contract between HttpConnection and HttpChannel

This commit is contained in:
Greg Wilkins 2012-05-22 22:28:26 +02:00
parent 40ed3a013a
commit b8517abb14
10 changed files with 411 additions and 344 deletions

View File

@ -167,6 +167,14 @@ public class HttpGenerator
return _contentPrepared;
}
/* ------------------------------------------------------------ */
public void abort()
{
_persistent=false;
_state=State.END;
_content=null;
}
/* ------------------------------------------------------------ */
public Result generate(Info info, ByteBuffer header, ByteBuffer chunk, ByteBuffer buffer, ByteBuffer content, Action action)
{

View File

@ -52,7 +52,7 @@ public class HttpParser
CHUNK_SIZE,
CHUNK_PARAMS,
CHUNK,
SEEKING_EOF
CLOSED
};
private final HttpHandler _handler;
@ -158,11 +158,17 @@ public class HttpParser
}
/* ------------------------------------------------------------ */
public boolean isIdle()
public boolean isStart()
{
return isState(State.START);
}
/* ------------------------------------------------------------ */
public boolean isClosed()
{
return isState(State.CLOSED);
}
/* ------------------------------------------------------------ */
public boolean isComplete()
{
@ -181,37 +187,6 @@ public class HttpParser
return _persistent;
}
/* ------------------------------------------------------------------------------- */
public void setPersistent(boolean persistent)
{
_persistent = persistent;
if (!_persistent &&(_state==State.END || _state==State.START))
_state=State.SEEKING_EOF;
}
/* ------------------------------------------------------------------------------- */
/**
* Parse until {@link #END END} state.
* 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.
*/
public void parseAll(ByteBuffer buffer) throws IOException
{
if (_state==State.END)
reset();
if (_state!=State.START)
throw new IllegalStateException("!START");
// continue parsing
while (_state != State.END && buffer.hasRemaining())
{
int remaining=buffer.remaining();
parseNext(buffer);
if (remaining==buffer.remaining())
break;
}
}
/* ------------------------------------------------------------------------------- */
/* Quick lookahead for the start state looking for a request method or a HTTP version,
* otherwise skip white space until something else to parse.
@ -238,6 +213,7 @@ public class HttpParser
if (_version!=null)
{
buffer.position(buffer.position()+_version.asString().length()+1);
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
_state=State.SPACE1;
return;
}
@ -328,7 +304,7 @@ public class HttpParser
badMessage(buffer, "Unknown Version");
return true;
}
_persistent=HttpVersion.HTTP_1_1==_version;
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
_state=State.SPACE1;
}
else if (ch < HttpTokens.SPACE && ch>=0)
@ -397,8 +373,8 @@ public class HttpParser
_utf8.reset();
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri,null);
_persistent=false;
_state=State.SEEKING_EOF;
return_from_parse|=_handler.headerComplete(false,false);
_state=State.END;
return_from_parse|=_handler.headerComplete(false,_persistent);
return_from_parse|=_handler.messageComplete(_contentPosition);
}
else
@ -428,7 +404,7 @@ public class HttpParser
_string.setLength(0);
buffer.position(buffer.position()+_version.asString().length()-1);
_eol=buffer.get();
_persistent=HttpVersion.HTTP_1_1==_version;
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
_state=State.HEADER;
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, _version);
}
@ -448,8 +424,8 @@ public class HttpParser
// HTTP/0.9
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, null);
_persistent=false;
_state=State.SEEKING_EOF;
return_from_parse|=_handler.headerComplete(false,false);
_state=State.END;
return_from_parse|=_handler.headerComplete(false,_persistent);
return_from_parse|=_handler.messageComplete(_contentPosition);
}
}
@ -467,7 +443,7 @@ public class HttpParser
}
_eol=ch;
_persistent=HttpVersion.HTTP_1_1==_version;
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
_state=State.HEADER;
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, _version);
continue;
@ -649,7 +625,8 @@ public class HttpParser
{
case EOF_CONTENT:
_state=State.EOF_CONTENT;
return_from_parse|=_handler.headerComplete(true,false);
_persistent=false;
return_from_parse|=_handler.headerComplete(true,_persistent);
break;
case CHUNKED_CONTENT:
@ -659,7 +636,7 @@ public class HttpParser
case NO_CONTENT:
return_from_parse|=_handler.headerComplete(false,_persistent);
_state=_persistent||(_responseStatus>=100&&_responseStatus<200)?State.END:State.SEEKING_EOF;
_state=State.END;
return_from_parse|=_handler.messageComplete(_contentPosition);
break;
@ -863,34 +840,41 @@ public class HttpParser
* Parse until next Event.
* @return True if an {@link RequestHandler} method was called and it returned true;
*/
public boolean parseNext(ByteBuffer buffer) throws IOException
public boolean parseNext(ByteBuffer buffer)
{
try
{
// process end states
if (_state == State.END)
// TODO should we consume white space here?
return false;
// handle initial state
switch(_state)
{
case START:
_version=null;
_method=null;
_methodString=null;
_uri=null;
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
_header=null;
quickStart(buffer);
break;
case CONTENT:
if (_contentPosition==_contentLength)
{
_state=State.END;
if(_handler.messageComplete(_contentPosition))
return true;
}
break;
case END:
return false;
case CLOSED:
BufferUtil.clear(buffer);
return false;
}
if (_state == State.CONTENT && _contentPosition == _contentLength)
{
// TODO why is this not _state=_persistent?State.END:State.SEEKING_EOF;
_state=State.END;
if(_handler.messageComplete(_contentPosition))
return true;
}
// Handle start
if (_state==State.START)
{
_version=null;
_method=null;
_methodString=null;
_uri=null;
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
_header=null;
quickStart(buffer);
}
// Request/response line
if (_state.ordinal()<State.HEADER.ordinal())
@ -904,7 +888,7 @@ public class HttpParser
// Handle HEAD response
if (_responseStatus>0 && _headResponse)
{
_state=_persistent||(_responseStatus>=100&&_responseStatus<200)?State.END:State.SEEKING_EOF;
_state=State.END;
if (_handler.messageComplete(_contentLength))
return true;
}
@ -936,7 +920,7 @@ public class HttpParser
long remaining=_contentLength - _contentPosition;
if (remaining == 0)
{
_state=_persistent?State.END:State.SEEKING_EOF;
_state=State.END;
if (_handler.messageComplete(_contentPosition))
return true;
}
@ -960,7 +944,7 @@ public class HttpParser
if(_contentPosition == _contentLength)
{
_state=_persistent?State.END:State.SEEKING_EOF;
_state=State.END;
if (_handler.messageComplete(_contentPosition))
return true;
}
@ -995,7 +979,7 @@ public class HttpParser
{
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
_eol=buffer.get();
_state=_persistent?State.END:State.SEEKING_EOF;
_state=State.END;
if (_handler.messageComplete(_contentPosition))
return true;
}
@ -1025,7 +1009,7 @@ public class HttpParser
{
if (_eol==HttpTokens.CARRIAGE_RETURN && buffer.hasRemaining() && buffer.get(buffer.position())==HttpTokens.LINE_FEED)
_eol=buffer.get();
_state=_persistent?State.END:State.SEEKING_EOF;
_state=State.END;
if (_handler.messageComplete(_contentPosition))
return true;
}
@ -1058,12 +1042,6 @@ public class HttpParser
}
break;
}
case SEEKING_EOF:
{
buffer.clear().limit(0);
break;
}
}
}
@ -1082,7 +1060,7 @@ public class HttpParser
{
BufferUtil.clear(buffer);
_persistent=false;
_state=State.SEEKING_EOF;
_state=State.END;
_handler.badMessage(reason);
}
@ -1095,8 +1073,6 @@ public class HttpParser
switch(_state)
{
case END:
case SEEKING_EOF:
_state=State.END;
break;
case EOF_CONTENT:
@ -1111,18 +1087,26 @@ public class HttpParser
_handler.messageComplete(_contentPosition);
}
if (!isComplete() && !isIdle())
if (!isComplete() && !isStart())
throw new EofException();
return true;
}
/* ------------------------------------------------------------------------------- */
public void close()
{
if (_state!=State.END)
throw new IllegalStateException(toString());
_persistent=false;
reset();
}
/* ------------------------------------------------------------------------------- */
public void reset()
{
// reset state
// TODO why is this not _state=_persistent?State.START:(_state=State.SEEKING_EOF);
_state=_persistent?State.START:(_state==State.END?State.END:State.SEEKING_EOF);
_state=_persistent?State.START:State.CLOSED;
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
_contentPosition=0;
_responseStatus=0;
@ -1140,10 +1124,11 @@ public class HttpParser
@Override
public String toString()
{
return String.format("%s{s=%s,c=%d}",
return String.format("%s{s=%s,c=%d,p=%b}",
getClass().getSimpleName(),
_state,
_contentLength);
_contentLength,
_persistent);
}
/* ------------------------------------------------------------ */

View File

@ -336,16 +336,7 @@ public class HttpGeneratorServerTest
parser=new HttpParser(handler);
parser.setHeadResponse(tr[r]._head);
try
{
parser.parseNext(BufferUtil.toBuffer(response));
}
catch(IOException e)
{
if (tr[r]._body!=null)
throw new Exception(t,e);
continue;
}
parser.parseNext(BufferUtil.toBuffer(response));
if (tr[r]._body!=null)
assertEquals(t,tr[r]._body, this._content);

View File

@ -19,6 +19,7 @@ import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpParser.State;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.junit.Assert;
@ -30,6 +31,31 @@ import org.junit.Test;
*/
public class HttpParserTest
{
/* ------------------------------------------------------------------------------- */
/**
* Parse until {@link #END END} state.
* If the parser is already in the END state, then it is {@link #reset reset} and re-parsed.
* @param parser TODO
* @throws IllegalStateException If the buffers have already been partially parsed.
*/
public static void parseAll(HttpParser parser, ByteBuffer buffer)
{
if (parser.isState(State.END))
parser.reset();
if (!parser.isState(State.START))
throw new IllegalStateException("!START");
// continue parsing
while (!parser.isState(State.END) && buffer.hasRemaining())
{
int remaining=buffer.remaining();
parser.parseNext(buffer);
if (remaining==buffer.remaining())
break;
}
}
@Test
public void testLineParse0() throws Exception
{
@ -37,11 +63,11 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
assertEquals("POST", f0);
assertEquals("/foo", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(-1, h);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/foo", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(-1, _h);
}
@Test
@ -49,14 +75,14 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("GET /999\015\012");
f2= null;
_versionOrReason= null;
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
assertEquals("GET", f0);
assertEquals("/999", f1);
assertEquals(null, f2);
assertEquals(-1, h);
parseAll(parser,buffer);
assertEquals("GET", _methodOrVersion);
assertEquals("/999", _uriOrStatus);
assertEquals(null, _versionOrReason);
assertEquals(-1, _h);
}
@Test
@ -64,14 +90,14 @@ public class HttpParserTest
{
ByteBuffer buffer= BufferUtil.toBuffer("POST /222 \015\012");
f2= null;
_versionOrReason= null;
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
assertEquals("POST", f0);
assertEquals("/222", f1);
assertEquals(null, f2);
assertEquals(-1, h);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/222", _uriOrStatus);
assertEquals(null, _versionOrReason);
assertEquals(-1, _h);
}
@Test
@ -81,11 +107,11 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
assertEquals("POST", f0);
assertEquals("/fo\u0690", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(-1, h);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/fo\u0690", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(-1, _h);
}
@Test
@ -95,11 +121,11 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
assertEquals("POST", f0);
assertEquals("/foo?param=\u0690", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(-1, h);
parseAll(parser,buffer);
assertEquals("POST", _methodOrVersion);
assertEquals("/foo?param=\u0690", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(-1, _h);
}
@Test
@ -108,12 +134,12 @@ public class HttpParserTest
ByteBuffer buffer= BufferUtil.toBuffer("CONNECT 192.168.1.2:80 HTTP/1.1\015\012" + "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
parseAll(parser,buffer);
assertTrue(handler.request);
assertEquals("CONNECT", f0);
assertEquals("192.168.1.2:80", f1);
assertEquals("HTTP/1.1", f2);
assertEquals(-1, h);
assertEquals("CONNECT", _methodOrVersion);
assertEquals("192.168.1.2:80", _uriOrStatus);
assertEquals("HTTP/1.1", _versionOrReason);
assertEquals(-1, _h);
}
@Test
@ -133,26 +159,26 @@ public class HttpParserTest
"\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
parseAll(parser,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("Header 2", 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("Host Header", hdr[6]);
assertEquals("notHost", val[6]);
assertEquals(6, h);
assertEquals("GET", _methodOrVersion);
assertEquals("/", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals("Host", _hdr[0]);
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("Header 2", _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("Host Header", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals(6, _h);
}
@Test
@ -191,22 +217,22 @@ public class HttpParserTest
parser.parseNext(buffer);
}
assertEquals("SPLIT", 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);
assertEquals("SPLIT", _methodOrVersion);
assertEquals("/", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
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);
}
}
@ -226,14 +252,14 @@ public class HttpParserTest
+ "0\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseAll(buffer);
parseAll(parser,buffer);
assertEquals("GET", f0);
assertEquals("/chunk", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(1, h);
assertEquals("Header1", hdr[0]);
assertEquals("value1", val[0]);
assertEquals("GET", _methodOrVersion);
assertEquals("/chunk", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(1, _h);
assertEquals("Header1", _hdr[0]);
assertEquals("value1", _val[0]);
assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
}
@ -241,7 +267,7 @@ public class HttpParserTest
public void testMultiParse() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET /mp HTTP/1.0\015\012"
"GET /mp HTTP/1.0\015\012"
+ "Connection: Keep-Alive\015\012"
+ "Header1: value1\015\012"
+ "Transfer-Encoding: chunked\015\012"
@ -251,11 +277,15 @@ public class HttpParserTest
+ "1a\015\012"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ\015\012"
+ "0\015\012"
+ "\015\012"
+ "POST /foo HTTP/1.0\015\012"
+ "Connection: Keep-Alive\015\012"
+ "Header2: value2\015\012"
+ "Content-Length: 0\015\012"
+ "\015\012"
+ "PUT /doodle HTTP/1.0\015\012"
+ "Connection: close\015\012"
+ "Header3: value3\015\012"
@ -267,33 +297,35 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("GET", f0);
assertEquals("/mp", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(2, h);
assertEquals("Header1", hdr[1]);
assertEquals("value1", val[1]);
assertEquals("GET", _methodOrVersion);
assertEquals("/mp", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(2, _h);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", _content);
parser.reset();
init();
parser.parseNext(buffer);
assertEquals("POST", f0);
assertEquals("/foo", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(2, h);
assertEquals("Header2", hdr[1]);
assertEquals("value2", val[1]);
assertEquals("POST", _methodOrVersion);
assertEquals("/foo", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(2, _h);
assertEquals("Header2", _hdr[1]);
assertEquals("value2", _val[1]);
assertEquals(null, _content);
parser.reset();
init();
parser.parseNext(buffer);
parser.inputShutdown();
assertEquals("PUT", f0);
assertEquals("/doodle", f1);
assertEquals("HTTP/1.0", f2);
assertEquals(2, h);
assertEquals("Header3", hdr[1]);
assertEquals("value3", val[1]);
assertEquals("PUT", _methodOrVersion);
assertEquals("/doodle", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
assertEquals(2, _h);
assertEquals("Header3", _hdr[1]);
assertEquals("value3", _val[1]);
assertEquals("0123456789", _content);
}
@ -310,12 +342,12 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("200", f1);
assertEquals("Correct", f2);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
assertEquals("Correct", _versionOrReason);
assertEquals(10,_content.length());
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@Test
@ -329,20 +361,21 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("304", f1);
assertEquals("Not-Modified", f2);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("304", _uriOrStatus);
assertEquals("Not-Modified", _versionOrReason);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@Test
public void testResponseParse2() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"HTTP/1.1 204 No-Content\015\012"
"HTTP/1.1 204 No-Content\015\012"
+ "Header: value\015\012"
+ "\015\012"
+ "HTTP/1.1 200 Correct\015\012"
+ "Content-Length: 10\015\012"
+ "Content-Type: text/plain\015\012"
@ -352,23 +385,23 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("204", f1);
assertEquals("No-Content", f2);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("204", _uriOrStatus);
assertEquals("No-Content", _versionOrReason);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
parser.setPersistent(true);
parser.reset();
init();
parser.parseNext(buffer);
parser.inputShutdown();
assertEquals("HTTP/1.1", f0);
assertEquals("200", f1);
assertEquals("Correct", f2);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
assertEquals("Correct", _versionOrReason);
assertEquals(_content.length(), 10);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@ -385,12 +418,12 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("200", f1);
assertEquals(null, f2);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
assertEquals(null, _versionOrReason);
assertEquals(_content.length(), 10);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@Test
@ -406,12 +439,12 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("200", f1);
assertEquals(null, f2);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
assertEquals(null, _versionOrReason);
assertEquals(_content.length(), 10);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@Test
@ -425,12 +458,12 @@ public class HttpParserTest
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("304", f1);
assertEquals("found", f2);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("304", _uriOrStatus);
assertEquals("found", _versionOrReason);
assertEquals(null,_content);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@Test
@ -449,12 +482,12 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", f0);
assertEquals("200", f1);
assertEquals("OK", f2);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("200", _uriOrStatus);
assertEquals("OK", _versionOrReason);
assertEquals(null,_content);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@ -472,10 +505,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals(null,f0);
assertEquals(null,_methodOrVersion);
assertEquals("No URI",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@ -492,10 +525,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals(null,f0);
assertEquals(null,_methodOrVersion);
assertEquals("No URI",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -511,10 +544,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals(null,f0);
assertEquals(null,_methodOrVersion);
assertEquals("Unknown Version",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -530,10 +563,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals(null,f0);
assertEquals(null,_methodOrVersion);
assertEquals("No Status",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -549,10 +582,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals(null,f0);
assertEquals(null,_methodOrVersion);
assertEquals("No Status",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -568,10 +601,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals(null,f0);
assertEquals(null,_methodOrVersion);
assertEquals("Unknown Version",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -587,10 +620,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("GET",f0);
assertEquals("GET",_methodOrVersion);
assertEquals("Bad Content-Length",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -606,10 +639,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("GET",f0);
assertEquals("GET",_methodOrVersion);
assertEquals("Bad Content-Length",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@Test
@ -625,10 +658,10 @@ public class HttpParserTest
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("GET",f0);
assertEquals("GET",_methodOrVersion);
assertEquals("Bad Content-Length",_bad);
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.SEEKING_EOF,parser.getState());
assertEquals(HttpParser.State.END,parser.getState());
}
@ -637,25 +670,27 @@ public class HttpParserTest
{
_bad=null;
_content=null;
f0=null;
f1=null;
f2=null;
hdr=null;
val=null;
h=0;
_methodOrVersion=null;
_uriOrStatus=null;
_versionOrReason=null;
_hdr=null;
_val=null;
_h=0;
_headerCompleted=false;
_messageCompleted=false;
}
private String _bad;
private String _content;
private String f0;
private String f1;
private String f2;
private String[] hdr;
private String[] val;
private int h;
private String _methodOrVersion;
private String _uriOrStatus;
private String _versionOrReason;
private String[] _hdr;
private String[] _val;
private int _h;
private boolean headerCompleted;
private boolean messageCompleted;
private boolean _headerCompleted;
private boolean _messageCompleted;
private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler
{
@ -678,16 +713,16 @@ public class HttpParserTest
public boolean startRequest(HttpMethod httpMethod, String method, String uri, HttpVersion version)
{
request=true;
h= -1;
hdr= new String[9];
val= new String[9];
f0= method;
f1= uri;
f2= version==null?null:version.asString();
_h= -1;
_hdr= new String[9];
_val= new String[9];
_methodOrVersion= method;
_uriOrStatus= uri;
_versionOrReason= version==null?null:version.asString();
fields=new HttpFields();
messageCompleted = false;
headerCompleted = false;
_messageCompleted = false;
_headerCompleted = false;
return false;
}
@ -695,8 +730,8 @@ public class HttpParserTest
public boolean parsedHeader(HttpHeader header, String name, String value)
{
//System.err.println("header "+name+": "+value);
hdr[++h]= name;
val[h]= value;
_hdr[++_h]= name;
_val[_h]= value;
return false;
}
@ -714,7 +749,7 @@ public class HttpParserTest
throw new IllegalStateException();
}
headerCompleted = true;
_headerCompleted = true;
return false;
}
@ -722,7 +757,7 @@ public class HttpParserTest
public boolean messageComplete(long contentLength)
{
//System.err.println("messageComplete");
messageCompleted = true;
_messageCompleted = true;
return true;
}
@ -736,16 +771,16 @@ public class HttpParserTest
public boolean startResponse(HttpVersion version, int status, String reason)
{
request=false;
f0 = version.asString();
f1 = Integer.toString(status);
f2 = reason==null?null:reason.toString();
_methodOrVersion = version.asString();
_uriOrStatus = Integer.toString(status);
_versionOrReason = reason==null?null:reason.toString();
fields=new HttpFields();
hdr= new String[9];
val= new String[9];
_hdr= new String[9];
_val= new String[9];
messageCompleted = false;
headerCompleted = false;
_messageCompleted = false;
_headerCompleted = false;
return false;
}

View File

@ -90,7 +90,7 @@ public abstract class HttpChannel
private int _requests;
private int _include;
private HttpVersion _version = HttpVersion.HTTP_1_1;
private boolean _expect = false;
@ -285,6 +285,9 @@ public abstract class HttpChannel
/* ------------------------------------------------------------ */
public void reset()
{
_expect=false;
_expect100Continue=false;
_expect102Processing=false;
_requestFields.clear();
_request.recycle();
_responseFields.clear();
@ -295,9 +298,9 @@ public abstract class HttpChannel
}
/* ------------------------------------------------------------ */
protected void handleRequest()
protected void process()
{
LOG.debug("{} handleRequest",this);
LOG.debug("{} process",this);
String threadName=null;
if (LOG.isDebugEnabled())
@ -306,8 +309,6 @@ public abstract class HttpChannel
Thread.currentThread().setName(threadName+" - "+_uri);
}
Throwable async_exception=null;
__currentChannel.set(this);
try
{
@ -324,7 +325,6 @@ public abstract class HttpChannel
_request.setHandled(false);
_out.reopen();
if (_state.isInitial())
{
_request.setDispatcherType(DispatcherType.REQUEST);
@ -345,21 +345,21 @@ public abstract class HttpChannel
catch (EofException e)
{
LOG.debug(e);
async_exception=e;
_state.error(e);
_request.setHandled(true);
}
catch (ServletException e)
{
LOG.warn(String.valueOf(_uri),e.toString());
LOG.debug(String.valueOf(_uri),e);
async_exception=e;
_state.error(e);
_request.setHandled(true);
commitError(500, null, e.toString());
}
catch (Throwable e)
{
LOG.warn(String.valueOf(_uri),e);
async_exception=e;
_state.error(e);
_request.setHandled(true);
commitError(500, null, e.toString());
}
@ -377,22 +377,20 @@ public abstract class HttpChannel
if (_state.isUncompleted())
{
_state.doComplete(async_exception);
if (_expect100Continue)
{
LOG.debug("100 continues not sent");
// We didn't send 100 continues, but the latest interpretation
// of the spec (see httpbis) is that the client will either
// send the body anyway, or close. So we no longer need to
// do anything special here other than make the connection not persistent
_expect100Continue = false;
if (!_response.isCommitted())
_response.addHeader(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE.toString());
}
try
{
if (_expect100Continue)
{
LOG.debug("100 continues not sent");
// We didn't send 100 continues, but the latest interpretation
// of the spec (see httpbis) is that the client will either
// send the body anyway, or close. So we no longer need to
// do anything special here other than make the connection not persistent
_expect100Continue = false;
if (!_response.isCommitted())
_response.addHeader(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE.toString());
}
if (!_response.isCommitted() && !_request.isHandled())
_response.sendError(404);
_response.complete();
@ -401,8 +399,12 @@ public abstract class HttpChannel
{
LOG.warn(e);
}
_request.setHandled(true);
completed();
finally
{
_state.doComplete();
_request.setHandled(true);
completed();
}
}
}
}
@ -633,7 +635,7 @@ public abstract class HttpChannel
default:
}
// Either handle now or wait for first content
// Either handle now or wait for first content/message complete
if (_expect100Continue)
return true;

View File

@ -59,7 +59,7 @@ public class HttpChannelState implements AsyncContext, Continuation
// REDISPATCH REDISPATCHED
// REDISPATCHED ASYNCSTARTED UNCOMPLETED
// COMPLETING UNCOMPLETED UNCOMPLETED
// UNCOMPLETED COMPLETED
// UNCOMPLETED UNCOMPLETED COMPLETED
// COMPLETED
public enum State
@ -287,7 +287,7 @@ public class HttpChannelState implements AsyncContext, Continuation
/* ------------------------------------------------------------ */
/**
* @return false if the handling of the request should not proceed
* @return true if the handling of the request should proceed
*/
protected boolean handling()
{
@ -316,6 +316,7 @@ public class HttpChannelState implements AsyncContext, Continuation
_state=State.UNCOMPLETED;
return false;
case UNCOMPLETED:
case ASYNCWAIT:
return false;
@ -355,9 +356,9 @@ public class HttpChannelState implements AsyncContext, Continuation
}
_state=State.ASYNCSTARTED;
List<AsyncListener> recycle=_lastAsyncListeners;
List<AsyncListener> listeners=_lastAsyncListeners;
_lastAsyncListeners=_asyncListeners;
_asyncListeners=recycle;
_asyncListeners=listeners;
if (_asyncListeners!=null)
_asyncListeners.clear();
break;
@ -383,6 +384,18 @@ public class HttpChannelState implements AsyncContext, Continuation
}
}
/* ------------------------------------------------------------ */
protected void error(Throwable th)
{
synchronized (this)
{
// TODO should we change state here?
if (_event!=null)
_event._cause=th;
}
}
/* ------------------------------------------------------------ */
/**
* Signal that the HttpConnection has finished handling the request.
@ -600,7 +613,7 @@ public class HttpChannelState implements AsyncContext, Continuation
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#complete()
*/
protected void doComplete(Throwable ex)
protected void doComplete()
{
final List<ContinuationListener> cListeners;
final List<AsyncListener> aListeners;
@ -627,10 +640,10 @@ public class HttpChannelState implements AsyncContext, Continuation
{
try
{
if (ex!=null)
if (_event!=null && _event._cause!=null)
{
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,ex.getMessage());
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,_event._cause);
_event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,_event._cause.getMessage());
listener.onError(_event);
}
else
@ -678,6 +691,8 @@ public class HttpChannelState implements AsyncContext, Continuation
cancelTimeout();
_timeoutMs=DEFAULT_TIMEOUT;
_continuationListeners=null;
if (_event!=null)
_event._cause=null;
}
}
@ -1004,6 +1019,7 @@ public class HttpChannelState implements AsyncContext, Continuation
private final ServletContext _suspendedContext;
private ServletContext _dispatchContext;
private String _path;
private Throwable _cause;
public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
{
@ -1037,7 +1053,7 @@ public class HttpChannelState implements AsyncContext, Continuation
@Override
public void run()
{
_channel.handleRequest();
_channel.process();
}
};
}

View File

@ -46,7 +46,7 @@ public class HttpConnection extends AbstractAsyncConnection
private final HttpGenerator _generator;
private final HttpChannel _channel;
private final ByteBufferPool _bufferPool;
private final HttpInput _httpInput;
private final HttpHttpInput _httpInput;
private ResponseInfo _info;
ByteBuffer _requestBuffer=null;
@ -129,7 +129,11 @@ public class HttpConnection extends AbstractAsyncConnection
/* ------------------------------------------------------------ */
public void reset()
{
_parser.reset();
if (_generator.isPersistent())
_parser.reset();
else
_parser.close();
_generator.reset();
_channel.reset();
_httpInput.recycle();
@ -247,10 +251,10 @@ public class HttpConnection extends AbstractAsyncConnection
// to handle a request. Call the channel and this will either handle the
// request/response to completion OR if the request suspends, the channel
// will be left in !idle state so our outer loop will exit.
_channel.handleRequest();
_channel.process();
// Return if the channel is still processing the request
if (!_channel.isIdle())
if (_channel.isSuspended())
{
// release buffer if all input has been consumed
if (_httpInput.available()==0)
@ -265,11 +269,6 @@ public class HttpConnection extends AbstractAsyncConnection
return;
}
}
else if (BufferUtil.hasContent(_requestBuffer))
{
LOG.warn("STATE MACHINE FAILURE??? {} {}",_parser,BufferUtil.toDetailString(_requestBuffer));
BufferUtil.clear(_requestBuffer);
}
}
}
catch(Exception e)
@ -367,7 +366,15 @@ public class HttpConnection extends AbstractAsyncConnection
{
// We could not send the error, so a sudden close of the connection will at least tell
// the client something is wrong
getEndPoint().close();
if (BufferUtil.hasContent(_responseBuffer))
{
BufferUtil.clear(_responseBuffer);
releaseRequestBuffer();
}
_generator.abort();
return false;
}
return true;
@ -376,7 +383,15 @@ public class HttpConnection extends AbstractAsyncConnection
@Override
protected void completed()
{
LOG.debug(BufferUtil.toDetailString(_requestBuffer));
LOG.debug("{} completed");
// parser should be either complete or can be completed with single call
if (!_parser.isComplete())
{
// TODO make this much more efficient
_httpInput.consumeAll();
}
HttpConnection.this.reset();
// if the onReadable method is not executing
@ -384,7 +399,7 @@ public class HttpConnection extends AbstractAsyncConnection
{
// TODO is there a race here?
if (_parser.isIdle())
if (_parser.isStart())
{
// it wants to eat more
if (_requestBuffer==null)
@ -398,7 +413,7 @@ public class HttpConnection extends AbstractAsyncConnection
});
}
}
else if (!getEndPoint().isOutputShutdown() && _parser.getState()==HttpParser.State.SEEKING_EOF)
else if (!getEndPoint().isOutputShutdown() && _parser.isState(HttpParser.State.CLOSED))
{
// TODO This is a catch all indicating some protocol handling failure
// Currently needed for requests saying they are HTTP/2.0.
@ -704,6 +719,27 @@ public class HttpConnection extends AbstractAsyncConnection
}
}
protected void consumeAll()
{
while (true)
{
synchronized (_inputQ)
{
_inputQ.clear();
}
if (_parser.isComplete())
return;
try
{
blockForContent();
}
catch(IOException e)
{
LOG.warn(e);
}
}
}
@Override
protected void onContentQueued(ByteBuffer ref)

View File

@ -38,8 +38,7 @@ import org.eclipse.jetty.util.BufferUtil;
public abstract class HttpInput extends ServletInputStream
{
protected final byte[] _oneByte=new byte[1];
private final ArrayQueue<ByteBuffer> _inputQ=new ArrayQueue<>();
protected final ArrayQueue<ByteBuffer> _inputQ=new ArrayQueue<>();
private ByteBuffer _content;
private boolean _inputEOF;

View File

@ -1247,7 +1247,9 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
public void testUnreadInput () throws Exception
{
configureServer(new NoopHandler());
final int REQS=5;
final int REQS=2;
char[] fill = new char[65*1024];
Arrays.fill(fill,'.');
String content="This is a loooooooooooooooooooooooooooooooooo"+
"ooooooooooooooooooooooooooooooooooooooooooooo"+
"ooooooooooooooooooooooooooooooooooooooooooooo"+
@ -1258,7 +1260,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"ooooooooooooooooooooooooooooooooooooooooooooo"+
"ooooooooooooooooooooooooooooooooooooooooooooo"+
"oooooooooooonnnnnnnnnnnnnnnnggggggggg content"+
new String(new char[65*1024]);
new String(fill);
final byte[] bytes = content.getBytes();
Socket client=newSocket(HOST,_connector.getLocalPort());
@ -1276,7 +1278,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
out.write(("Content-Length: "+bytes.length+"\r\n" + "\r\n").getBytes(StringUtil.__ISO_8859_1));
out.write(bytes,0,bytes.length);
}
out.write("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".getBytes(StringUtil.__ISO_8859_1));
out.write("GET / HTTP/1.1\r\nHost: last\r\nConnection: close\r\n\r\n".getBytes(StringUtil.__ISO_8859_1));
out.flush();
}
catch(Exception e)

View File

@ -12,14 +12,8 @@
// ========================================================================
package org.eclipse.jetty.server;
import static org.junit.Assert.assertTrue;
import java.io.OutputStream;
import java.net.Socket;
import org.eclipse.jetty.server.HttpServerTestFixture.EchoHandler;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* HttpServer Tester.
@ -33,17 +27,16 @@ public class SelectChannelServerTest extends HttpServerTestBase
}
@Override
public void testCommittedError() throws Exception
public void testRequest1() throws Exception
{
super.testCommittedError();
super.testRequest1();
}
@Override
public void testFragmentedChunk() throws Exception
public void testUnreadInput() throws Exception
{
super.testFragmentedChunk();
super.testUnreadInput();
}
}