diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 483918c26be..15120444b05 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -25,6 +25,7 @@ import static org.eclipse.jetty.http.HttpTokens.TAB; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.EnumSet; import org.eclipse.jetty.http.HttpTokens.EndOfContent; import org.eclipse.jetty.util.ArrayTernaryTrie; @@ -123,9 +124,13 @@ public class HttpParser CHUNK_PARAMS, CHUNK, END, - CLOSED + CLOSE, // The associated stream/endpoint should be closed + CLOSED // The associated stream/endpoint is at EOF } + private final static EnumSet __idleStates = EnumSet.of(State.START,State.END,State.CLOSE,State.CLOSED); + private final static EnumSet __completeStates = EnumSet.of(State.END,State.CLOSE,State.CLOSED); + private final boolean DEBUG=LOG.isDebugEnabled(); // Cache debug to help branch prediction private final HttpHandler _handler; private final RequestHandler _requestHandler; @@ -144,7 +149,6 @@ public class HttpParser /* ------------------------------------------------------------------------------- */ private volatile State _state=State.START; private volatile boolean _eof; - private volatile boolean _closed; private HttpMethod _method; private String _methodString; private HttpVersion _version; @@ -313,6 +317,12 @@ public class HttpParser return isState(State.START); } + /* ------------------------------------------------------------ */ + public boolean isClose() + { + return isState(State.CLOSE); + } + /* ------------------------------------------------------------ */ public boolean isClosed() { @@ -322,13 +332,13 @@ public class HttpParser /* ------------------------------------------------------------ */ public boolean isIdle() { - return isState(State.START)||isState(State.END)||isState(State.CLOSED); + return __idleStates.contains(_state); } /* ------------------------------------------------------------ */ public boolean isComplete() { - return isState(State.END)||isState(State.CLOSED); + return __completeStates.contains(_state); } /* ------------------------------------------------------------------------------- */ @@ -797,10 +807,8 @@ public class HttpParser case CONNECTION: // Don't cache if not persistent if (_valueString!=null && _valueString.contains("close")) - { - _closed=true; _connectionFields=null; - } + break; case AUTHORIZATION: @@ -1161,7 +1169,7 @@ public class HttpParser while (buffer.remaining()>0 && buffer.get(buffer.position())<=HttpTokens.SPACE) buffer.get(); } - else if (_state==State.CLOSED) + else if (_state==State.CLOSE || _state==State.CLOSED) { if (BufferUtil.hasContent(buffer)) { @@ -1190,6 +1198,7 @@ public class HttpParser break; case END: + case CLOSE: setState(State.CLOSED); break; @@ -1214,8 +1223,6 @@ public class HttpParser break; } } - - return false; } catch(BadMessageException e) { @@ -1229,28 +1236,25 @@ public class HttpParser LOG.warn("bad HTTP parsed: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler,e); else LOG.warn("bad HTTP parsed: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler); - setState(State.CLOSED); + setState(State.CLOSE); _handler.badMessage(e.getCode(), e.getReason()); - return false; } catch(NumberFormatException|IllegalStateException e) { BufferUtil.clear(buffer); - LOG.warn("parse exception: "+e.toString()+" for "+_handler); + LOG.warn("parse exception: {} in {} for {}",e.toString(),_state,_handler); if (DEBUG) LOG.debug(e); if (_state.ordinal()<=State.END.ordinal()) { - setState(State.CLOSED); + setState(State.CLOSE); _handler.badMessage(400,null); } else { _handler.earlyEOF(); - setState(State.CLOSED); + setState(State.CLOSE); } - - return false; } catch(Exception|Error e) { @@ -1260,17 +1264,16 @@ public class HttpParser if (_state.ordinal()<=State.END.ordinal()) { - setState(State.CLOSED); + setState(State.CLOSE); _handler.badMessage(400,null); } else { _handler.earlyEOF(); - setState(State.CLOSED); + setState(State.CLOSE); } - - return false; } + return false; } protected boolean parseContent(ByteBuffer buffer) @@ -1439,8 +1442,9 @@ public class HttpParser } /* ------------------------------------------------------------------------------- */ + /** Signal that the associated data source is at EOF + */ public void atEOF() - { if (DEBUG) LOG.debug("atEOF {}", this); @@ -1448,11 +1452,13 @@ public class HttpParser } /* ------------------------------------------------------------------------------- */ + /** Request that the associated data source be closed + */ public void close() { if (DEBUG) LOG.debug("close {}", this); - setState(State.CLOSED); + setState(State.CLOSE); } /* ------------------------------------------------------------------------------- */ @@ -1460,10 +1466,11 @@ public class HttpParser { if (DEBUG) LOG.debug("reset {}", this); + // reset state if (_state==State.CLOSED) return; - if (_closed) + if (_state==State.CLOSE) { setState(State.CLOSED); return; diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 8b1df5c6fbb..56d0a963e9e 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -1030,11 +1030,15 @@ public class HttpParserTest assertEquals(null,_content); assertTrue(_headerCompleted); assertTrue(_messageCompleted); - + + parser.close(); parser.reset(); parser.parseNext(buffer); assertFalse(buffer.hasRemaining()); - assertTrue(parser.isClosed()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); + assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1055,6 +1059,9 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("No URI",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1075,6 +1082,9 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("No URI",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1094,7 +1104,11 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("Unknown Version",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); + } @Test @@ -1113,6 +1127,9 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("No Status",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1132,6 +1149,9 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("No Status",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1151,7 +1171,10 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("Unknown Version",_bad); assertFalse(buffer.hasRemaining()); - assertEquals(HttpParser.State.CLOSED,parser.getState()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); + assertEquals(HttpParser.State.CLOSED,parser.getState()); buffer= BufferUtil.toBuffer( "GET / HTTP/1.01\015\012" @@ -1166,6 +1189,9 @@ public class HttpParserTest assertEquals(null,_methodOrVersion); assertEquals("Unknown Version",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1184,6 +1210,9 @@ public class HttpParserTest parser.parseNext(buffer); assertEquals("Bad EOL",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); @@ -1199,6 +1228,9 @@ public class HttpParserTest parser.parseNext(buffer); assertEquals("Bad EOL",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1221,6 +1253,9 @@ public class HttpParserTest assertEquals("GET",_methodOrVersion); assertEquals("Bad Content-Length",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1240,6 +1275,9 @@ public class HttpParserTest assertEquals("GET",_methodOrVersion); assertEquals("Bad Content-Length",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } @@ -1259,6 +1297,9 @@ public class HttpParserTest assertEquals("GET",_methodOrVersion); assertEquals("Bad Content-Length",_bad); assertFalse(buffer.hasRemaining()); + assertEquals(HttpParser.State.CLOSE,parser.getState()); + parser.atEOF(); + parser.parseNext(BufferUtil.EMPTY_BUFFER); assertEquals(HttpParser.State.CLOSED,parser.getState()); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index e7dee929a67..5be5c31e3ee 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -245,6 +245,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http // We are not in a race here for the request buffer as we have not yet received a request, // so there are not an possible legal threads calling #parseContent or #completed. releaseRequestBuffer(); + + // But we may have parsed an error condition, so let's check for close + if (_parser.isClose()) + close(); } } }