Merged branch 'jetty-9.3.x' into 'master'.

This commit is contained in:
Simone Bordet 2016-02-19 13:23:34 +01:00
commit 51009208d4
19 changed files with 311 additions and 191 deletions

View File

@ -23,8 +23,8 @@ import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.Connection;
@ -45,12 +45,14 @@ public abstract class HttpConnection implements Connection
private static final Logger LOG = Log.getLogger(HttpConnection.class);
private static final HttpField CHUNKED_FIELD = new HttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
private final AtomicInteger idleTimeoutState = new AtomicInteger();
private final HttpDestination destination;
private int idleTimeoutGuard;
private long idleTimeoutStamp;
protected HttpConnection(HttpDestination destination)
{
this.destination = destination;
this.idleTimeoutStamp = System.nanoTime();
}
public HttpClient getHttpClient()
@ -180,17 +182,12 @@ public abstract class HttpConnection implements Connection
// Forbid idle timeouts for the time window where
// the request is associated to the channel and sent.
// Use a counter to support multiplexed requests.
boolean send = false;
while (true)
boolean send;
synchronized (this)
{
int current = idleTimeoutState.get();
if (current < 0)
break;
if (idleTimeoutState.compareAndSet(current, current + 1))
{
send = true;
break;
}
send = idleTimeoutGuard >= 0;
if (send)
++idleTimeoutGuard;
}
if (send)
@ -207,7 +204,13 @@ public abstract class HttpConnection implements Connection
channel.release();
result = new SendFailure(new HttpRequestException("Could not associate request to connection", request), false);
}
idleTimeoutState.decrementAndGet();
synchronized (this)
{
--idleTimeoutGuard;
idleTimeoutStamp = System.nanoTime();
}
return result;
}
else
@ -216,11 +219,27 @@ public abstract class HttpConnection implements Connection
}
}
public boolean onIdleTimeout()
public boolean onIdleTimeout(long idleTimeout)
{
if (LOG.isDebugEnabled())
LOG.debug("Idle timeout state {} - {}", idleTimeoutState, this);
return idleTimeoutState.compareAndSet(0, -1);
synchronized (this)
{
if (idleTimeoutGuard == 0)
{
long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTimeoutStamp);
boolean idle = elapsed > idleTimeout / 2;
if (idle)
idleTimeoutGuard = -1;
if (LOG.isDebugEnabled())
LOG.debug("Idle timeout {}/{}ms - {}", elapsed, idleTimeout, this);
return idle;
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Idle timeout skipped - {}", this);
return false;
}
}
}
@Override

View File

@ -459,7 +459,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
@Override
public String toString()
{
return String.format("%s[%s]%x%s,queue=%d,pool=%s",
return String.format("%s[%s]@%x%s,queue=%d,pool=%s",
HttpDestination.class.getSimpleName(),
asString(),
hashCode(),

View File

@ -99,9 +99,10 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
@Override
public boolean onIdleExpired()
{
boolean close = delegate.onIdleTimeout();
long idleTimeout = getEndPoint().getIdleTimeout();
boolean close = delegate.onIdleTimeout(idleTimeout);
if (close)
close(new TimeoutException("Idle timeout " + getEndPoint().getIdleTimeout() + "ms"));
close(new TimeoutException("Idle timeout " + idleTimeout + "ms"));
return false;
}

View File

@ -170,7 +170,9 @@ public class HttpReceiverOverHTTPTest
HttpExchange exchange = newExchange();
FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
connection.getHttpChannel().receive();
// Simulate an idle timeout
// ByteArrayEndPoint has an idle timeout of 0 by default,
// so to simulate an idle timeout is enough to wait a bit.
Thread.sleep(100);
connection.onIdleExpired();
try

View File

@ -111,12 +111,12 @@ public class SslBytesServerTest extends SslBytesTest
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint)
return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance())
{
@Override
protected HttpParser newHttpParser()
protected HttpParser newHttpParser(HttpParser.Compliance compliance)
{
return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize())
return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize(),compliance)
{
@Override
public boolean parseNext(ByteBuffer buffer)

View File

@ -18,19 +18,19 @@ NAME=$(echo $(basename $0) | sed -e 's/^[SK][0-9]*//' -e 's/\.sh$//')
# Configuration files
#
# /etc/default/$NAME
# If it exists, this is read at the start of script. It may perform any
# If it exists, this is read at the start of script. It may perform any
# sequence of shell commands, like setting relevant environment variables.
#
# $HOME/.$NAMErc (e.g. $HOME/.jettyrc)
# If it exists, this is read at the start of script. It may perform any
# If it exists, this is read at the start of script. It may perform any
# sequence of shell commands, like setting relevant environment variables.
#
# /etc/$NAME.conf
# If found, and no configurations were given on the command line,
# the file will be used as this script's configuration.
# the file will be used as this script's configuration.
# Each line in the file may contain:
# - A comment denoted by the pound (#) sign as first non-blank character.
# - The path to a regular file, which will be passed to jetty as a
# - The path to a regular file, which will be passed to jetty as a
# config.xml file.
# - The path to a directory. Each *.xml file in the directory will be
# passed to jetty as a config.xml file.
@ -62,10 +62,10 @@ NAME=$(echo $(basename $0) | sed -e 's/^[SK][0-9]*//' -e 's/\.sh$//')
# Where the $NAME.pid file should be stored. It defaults to the
# first available of /var/run, /usr/var/run, JETTY_BASE and /tmp
# if not set.
#
#
# JETTY_PID
# The Jetty PID file, defaults to $JETTY_RUN/$NAME.pid
#
#
# JETTY_ARGS
# The default arguments to pass to jetty.
# For example
@ -97,10 +97,10 @@ findDirectory()
local L OP=$1
shift
for L in "$@"; do
[ "$OP" "$L" ] || continue
[ "$OP" "$L" ] || continue
printf %s "$L"
break
done
done
}
running()
@ -118,7 +118,7 @@ running()
started()
{
# wait for 60s to see "STARTED" in PID file, needs jetty-started.xml as argument
for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15
for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15
do
sleep 4
[ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0
@ -162,12 +162,12 @@ shift
##################################################
ETC=/etc
if [ $UID != 0 ]
then
then
ETC=$HOME/etc
fi
for CONFIG in {/etc,~/etc}/default/${NAME}{,9} $HOME/.${NAME}rc; do
if [ -f "$CONFIG" ] ; then
if [ -f "$CONFIG" ] ; then
readConfig "$CONFIG"
fi
done
@ -187,7 +187,7 @@ JETTY_INSTALL_TRACE_FILE="start.jar"
##################################################
# Try to determine JETTY_HOME if not set
##################################################
if [ -z "$JETTY_HOME" ]
if [ -z "$JETTY_HOME" ]
then
JETTY_SH=$0
case "$JETTY_SH" in
@ -200,7 +200,7 @@ then
esac
if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
then
then
JETTY_HOME=
fi
fi
@ -210,7 +210,7 @@ fi
# No JETTY_HOME yet? We're out of luck!
##################################################
if [ -z "$JETTY_HOME" ]; then
echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location"
echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location"
exit 1
fi
@ -219,7 +219,7 @@ JETTY_HOME=$PWD
##################################################
# Set JETTY_BASE
# Set JETTY_BASE
##################################################
if [ -z "$JETTY_BASE" ]; then
JETTY_BASE=$JETTY_HOME
@ -232,7 +232,7 @@ JETTY_BASE=$PWD
#####################################################
# Check that jetty is where we think it is
#####################################################
if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ]
then
echo "** ERROR: Oops! Jetty doesn't appear to be installed in $JETTY_HOME"
echo "** ERROR: $JETTY_HOME/$JETTY_INSTALL_TRACE_FILE is not readable!"
@ -244,7 +244,7 @@ fi
# but only if no configurations were given on the
# command line.
##################################################
if [ -z "$JETTY_CONF" ]
if [ -z "$JETTY_CONF" ]
then
if [ -f $ETC/${NAME}.conf ]
then
@ -261,7 +261,7 @@ fi
#####################################################
# Find a location for the pid file
#####################################################
if [ -z "$JETTY_RUN" ]
if [ -z "$JETTY_RUN" ]
then
JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)
fi
@ -269,12 +269,12 @@ fi
#####################################################
# Find a pid and state file
#####################################################
if [ -z "$JETTY_PID" ]
if [ -z "$JETTY_PID" ]
then
JETTY_PID="$JETTY_RUN/${NAME}.pid"
fi
if [ -z "$JETTY_STATE" ]
if [ -z "$JETTY_STATE" ]
then
JETTY_STATE=$JETTY_BASE/${NAME}.state
fi
@ -289,7 +289,7 @@ JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE")
##################################################
# Get the list of config.xml files from jetty.conf
##################################################
if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ]
if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ]
then
while read -r CONF
do
@ -297,18 +297,18 @@ then
continue
fi
if [ -d "$CONF" ]
if [ -d "$CONF" ]
then
# assume it's a directory with configure.xml files
# for example: /etc/jetty.d/
# sort the files before adding them to the list of JETTY_ARGS
for XMLFILE in "$CONF/"*.xml
do
if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ]
if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ]
then
JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE")
else
echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'"
echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'"
fi
done
else
@ -335,11 +335,11 @@ fi
#####################################################
# See if JETTY_LOGS is defined
#####################################################
if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_BASE/logs ]
if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_BASE/logs ]
then
JETTY_LOGS=$JETTY_BASE/logs
fi
if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_HOME/logs ]
if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_HOME/logs ]
then
JETTY_LOGS=$JETTY_HOME/logs
fi
@ -367,7 +367,7 @@ esac
#####################################################
case "`uname`" in
CYGWIN*)
CYGWIN*)
JETTY_HOME="`cygpath -w $JETTY_HOME`"
JETTY_BASE="`cygpath -w $JETTY_BASE`"
TMPDIR="`cygpath -w $TMPDIR`"
@ -397,7 +397,7 @@ RUN_ARGS=(${JAVA_OPTIONS[@]} -jar "$JETTY_START" ${JETTY_ARGS[*]})
RUN_CMD=("$JAVA" ${RUN_ARGS[@]})
#####################################################
# Comment these out after you're happy with what
# Comment these out after you're happy with what
# the script is doing.
#####################################################
if (( DEBUG ))
@ -422,12 +422,12 @@ case "$ACTION" in
start)
echo -n "Starting Jetty: "
if (( NO_START )); then
if (( NO_START )); then
echo "Not starting ${NAME} - NO_START=1";
exit
fi
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1
then
unset CH_USER
if [ -n "$JETTY_USER" ]
@ -487,7 +487,7 @@ case "$ACTION" in
echo -n "Stopping Jetty: "
if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then
start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP
TIMEOUT=30
while running "$JETTY_PID"; do
if (( TIMEOUT-- == 0 )); then
@ -508,7 +508,7 @@ case "$ACTION" in
exit 1
fi
kill "$PID" 2>/dev/null
TIMEOUT=30
while running $JETTY_PID; do
if (( TIMEOUT-- == 0 )); then
@ -563,7 +563,13 @@ case "$ACTION" in
;;
check|status)
echo "Checking arguments to Jetty: "
if running "$JETTY_PID"
then
echo "Jetty running pid=$(< "$JETTY_PID")"
else
echo "Jetty NOT running"
fi
echo
echo "START_INI = $START_INI"
echo "START_D = $START_D"
echo "JETTY_HOME = $JETTY_HOME"
@ -579,10 +585,9 @@ case "$ACTION" in
echo "JETTY_ARGS = ${JETTY_ARGS[*]}"
echo "RUN_CMD = ${RUN_CMD[*]}"
echo
if running "$JETTY_PID"
then
echo "Jetty running pid=$(< "$JETTY_PID")"
exit 0
fi
exit 1

View File

@ -193,11 +193,12 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
@Override
public boolean onIdleExpired()
{
boolean close = delegate.onIdleTimeout();
long idleTimeout = getEndPoint().getIdleTimeout();
boolean close = delegate.onIdleTimeout(idleTimeout);
if (multiplexed)
close &= isFillInterested();
if (close)
close(new TimeoutException("Idle timeout " + getEndPoint().getIdleTimeout() + "ms"));
close(new TimeoutException("Idle timeout " + idleTimeout + "ms"));
return false;
}

View File

@ -74,10 +74,13 @@ import static org.eclipse.jetty.http.HttpTokens.TAB;
* is used to help the parsing of subsequent messages.
* </p>
* <p>
* If the system property "org.eclipse.jetty.http.HttpParser.STRICT" is set to true,
* then the parser will strictly pass on the exact strings received for methods and header
* fields. Otherwise a fast case insensitive string lookup is used that may alter the
* case of the method and/or headers
* The parser can work in varying compliance modes:
* <dl>
* <dt>RFC7230</dt><dd>(default) Compliance with RFC7230</dd>
* <dt>RFC2616</dt><dd>Wrapped headers supported</dd>
* <dt>STRICT</dt><dd>(misnomer) Adherence to Servlet Specification requirement for
* exact case of header names, bypassing the header caches, which are case insensitive,
* otherwise equivalent to RFC2616</dd>
* </p>
* <p>
* @see <a href="http://tools.ietf.org/html/rfc7230">RFC 7230</a>
@ -85,7 +88,8 @@ import static org.eclipse.jetty.http.HttpTokens.TAB;
public class HttpParser
{
public static final Logger LOG = Log.getLogger(HttpParser.class);
public final static boolean __STRICT=Boolean.getBoolean("org.eclipse.jetty.http.HttpParser.STRICT");
@Deprecated
public final static String __STRICT="org.eclipse.jetty.http.HttpParser.STRICT";
public final static int INITIAL_URI_LENGTH=256;
/**
@ -102,6 +106,9 @@ public class HttpParser
*/
public final static Trie<HttpField> CACHE = new ArrayTrie<>(2048);
// Compliance
public enum Compliance { STRICT, RFC2616, RFC7230 };
// States
public enum State
{
@ -139,7 +146,7 @@ public class HttpParser
private final RequestHandler _requestHandler;
private final ResponseHandler _responseHandler;
private final int _maxHeaderBytes;
private final boolean _strict;
private final Compliance _compliance;
private HttpField _field;
private HttpHeader _header;
private String _headerString;
@ -218,49 +225,69 @@ public class HttpParser
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
}
private static Compliance compliance()
{
Boolean strict = Boolean.getBoolean(__STRICT);
return strict?Compliance.STRICT:Compliance.RFC7230;
}
/* ------------------------------------------------------------------------------- */
public HttpParser(RequestHandler handler)
{
this(handler,-1,__STRICT);
this(handler,-1,compliance());
}
/* ------------------------------------------------------------------------------- */
public HttpParser(ResponseHandler handler)
{
this(handler,-1,__STRICT);
this(handler,-1,compliance());
}
/* ------------------------------------------------------------------------------- */
public HttpParser(RequestHandler handler,int maxHeaderBytes)
{
this(handler,maxHeaderBytes,__STRICT);
this(handler,maxHeaderBytes,compliance());
}
/* ------------------------------------------------------------------------------- */
public HttpParser(ResponseHandler handler,int maxHeaderBytes)
{
this(handler,maxHeaderBytes,__STRICT);
this(handler,maxHeaderBytes,compliance());
}
/* ------------------------------------------------------------------------------- */
@Deprecated
public HttpParser(RequestHandler handler,int maxHeaderBytes,boolean strict)
{
this(handler,maxHeaderBytes,strict?Compliance.STRICT:compliance());
}
/* ------------------------------------------------------------------------------- */
@Deprecated
public HttpParser(ResponseHandler handler,int maxHeaderBytes,boolean strict)
{
this(handler,maxHeaderBytes,strict?Compliance.STRICT:compliance());
}
/* ------------------------------------------------------------------------------- */
public HttpParser(RequestHandler handler,int maxHeaderBytes,Compliance compliance)
{
_handler=handler;
_requestHandler=handler;
_responseHandler=null;
_maxHeaderBytes=maxHeaderBytes;
_strict=strict;
_compliance=compliance==null?compliance():compliance;
}
/* ------------------------------------------------------------------------------- */
public HttpParser(ResponseHandler handler,int maxHeaderBytes,boolean strict)
public HttpParser(ResponseHandler handler,int maxHeaderBytes,Compliance compliance)
{
_handler=handler;
_requestHandler=null;
_responseHandler=handler;
_maxHeaderBytes=maxHeaderBytes;
_strict=strict;
_compliance=compliance==null?compliance():compliance;
}
/* ------------------------------------------------------------------------------- */
@ -556,7 +583,7 @@ public class HttpParser
_length=_string.length();
_methodString=takeString();
HttpMethod method=HttpMethod.CACHE.get(_methodString);
if (method!=null && !_strict)
if (method!=null && _compliance!=Compliance.STRICT)
_methodString=method.asString();
setState(State.SPACE1);
}
@ -832,7 +859,7 @@ public class HttpParser
_host=true;
if (!(_field instanceof HostPortHttpField))
{
_field=new HostPortHttpField(_header,_strict?_headerString:_header.asString(),_valueString);
_field=new HostPortHttpField(_header,_compliance==Compliance.STRICT?_headerString:_header.asString(),_valueString);
add_to_connection_trie=_connectionFields!=null;
}
break;
@ -861,7 +888,7 @@ public class HttpParser
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
{
if (_field==null)
_field=new HttpField(_header,_strict?_headerString:_header.asString(),_valueString);
_field=new HttpField(_header,_compliance==Compliance.STRICT?_headerString:_header.asString(),_valueString);
_connectionFields.put(_field);
}
}
@ -905,10 +932,32 @@ public class HttpParser
case HttpTokens.COLON:
case HttpTokens.SPACE:
case HttpTokens.TAB:
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Continuation");
{
if (_compliance.ordinal()>=Compliance.RFC7230.ordinal())
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Continuation");
// header value without name - continuation?
if (_valueString==null)
{
_string.setLength(0);
_length=0;
}
else
{
setString(_valueString);
_string.append(' ');
_length++;
_valueString=null;
}
setState(State.HEADER_VALUE);
break;
}
case HttpTokens.LINE_FEED:
{
// process previous header
parsedHeader();
_contentPosition=0;
// End of headers!
@ -967,9 +1016,13 @@ public class HttpParser
default:
{
// now handle the ch
if (ch<=HttpTokens.SPACE)
if (ch<HttpTokens.SPACE)
throw new BadMessageException();
// process previous header
parsedHeader();
// handle new header
if (buffer.hasRemaining())
{
// Try a look ahead for the known header name and value.
@ -982,7 +1035,7 @@ public class HttpParser
final String n;
final String v;
if (_strict)
if (_compliance==Compliance.STRICT)
{
// Have to get the fields exactly from the buffer to match case
String fn=field.getName();
@ -1106,7 +1159,6 @@ public class HttpParser
_valueString=null;
_length=-1;
parsedHeader();
setState(State.HEADER);
break;
}
@ -1135,7 +1187,6 @@ public class HttpParser
_valueString=takeString();
_length=-1;
}
parsedHeader();
setState(State.HEADER);
break;
}

View File

@ -218,6 +218,28 @@ public class HttpParserTest
assertEquals("close", _val[1]);
assertEquals(1, _headers);
}
@Test
public void test2616Continuations() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Name: value\015\012" +
" extra\015\012" +
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler,4096,HttpParser.Compliance.RFC2616);
parseAll(parser,buffer);
Assert.assertThat(_bad,Matchers.nullValue());
assertEquals("Host", _hdr[0]);
assertEquals("localhost", _val[0]);
assertEquals("Name", _hdr[1]);
assertEquals("value extra", _val[1]);
assertEquals(1, _headers);
}
@Test
public void test7230NoContinuations() throws Exception
@ -230,13 +252,12 @@ public class HttpParserTest
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
HttpParser parser= new HttpParser(handler,4096,HttpParser.Compliance.RFC7230);
parseAll(parser,buffer);
Assert.assertThat(_bad,Matchers.notNullValue());
Assert.assertThat(_bad,Matchers.containsString("Bad Continuation"));
}
@Test
public void test7230NoWhiteSpaceInName() throws Exception
@ -581,9 +602,9 @@ public class HttpParserTest
"cOnNeCtIoN: ClOsE\015\012"+
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler,-1,false);
HttpParser parser= new HttpParser(handler,-1,HttpParser.Compliance.RFC7230);
parseAll(parser,buffer);
assertNull(_bad);
assertEquals("GET", _methodOrVersion);
assertEquals("/", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
@ -603,9 +624,9 @@ public class HttpParserTest
"cOnNeCtIoN: ClOsE\015\012"+
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler,-1,true);
HttpParser parser= new HttpParser(handler,-1,HttpParser.Compliance.STRICT);
parseAll(parser,buffer);
assertNull(_bad);
assertEquals("gEt", _methodOrVersion);
assertEquals("/", _uriOrStatus);
assertEquals("HTTP/1.0", _versionOrReason);
@ -1562,26 +1583,6 @@ public class HttpParserTest
assertTrue(field==_fields.get(0));
}
@Test
public void testFolded() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Connection: close\015\012" +
"Content-Type: application/soap+xml; charset=utf-8; \015\012"+
"\taction=\"xxx\" \015\012" +
"\015\012");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser= new HttpParser(handler);
parseAll(parser,buffer);
assertFalse(_headerCompleted);
assertEquals(_bad, "Bad Continuation");
}
@Test
public void testParseRequest() throws Exception

View File

@ -615,7 +615,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
private void frame(HTTP2Flusher.Entry entry, boolean flush)
{
if (LOG.isDebugEnabled())
LOG.debug("Sending {}", entry.frame);
LOG.debug("{} {}", flush ? "Sending" : "Queueing", entry.frame);
// Ping frames are prepended to process them as soon as possible.
boolean queued = entry.frame.getType() == FrameType.PING ? flusher.prepend(entry) : flusher.append(entry);
if (queued && flush)

View File

@ -30,6 +30,7 @@ import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
@ -187,7 +188,7 @@ public class HttpClientTransportOverHTTP2 extends ContainerLifeCycle implements
@Override
public boolean onIdleTimeout(Session session)
{
return connection.onIdleTimeout();
return connection.onIdleTimeout(((HTTP2Session)session).getEndPoint().getIdleTimeout());
}
@Override

View File

@ -294,7 +294,7 @@ public class HTTP2CServerTest extends AbstractServerTest
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint)
HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance())
{
@Override
public void onFillable()

View File

@ -371,7 +371,7 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
protected void onIdleExpired(TimeoutException timeout)
{
Connection connection = _connection;
if (connection != null && !_connection.onIdleExpired())
if (connection != null && !connection.onIdleExpired())
return;
boolean output_shutdown=isOutputShutdown();

View File

@ -37,6 +37,7 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Locker;
/**
* Container class for the MBean instances
@ -51,6 +52,7 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
__unique.clear();
}
private final Locker _lock = new Locker();
private final MBeanServer _mbeanServer;
private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
private String _domain = null;
@ -63,8 +65,11 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
*/
public synchronized ObjectName findMBean(Object object)
{
ObjectName bean = _beans.get(object);
return bean == null ? null : bean;
try (Locker.Lock lock = _lock.lock())
{
ObjectName bean = _beans.get(object);
return bean == null ? null : bean;
}
}
/**
@ -75,11 +80,14 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
*/
public synchronized Object findBean(ObjectName oname)
{
for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
try (Locker.Lock lock = _lock.lock())
{
ObjectName bean = entry.getValue();
if (bean.equals(oname))
return entry.getKey();
for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
{
ObjectName bean = entry.getValue();
if (bean.equals(oname))
return entry.getKey();
}
}
return null;
}
@ -130,32 +138,31 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
{
if (LOG.isDebugEnabled())
LOG.debug("beanAdded {}->{}",parent,obj);
// Is their an object name for the parent
ObjectName pname=null;
if (parent!=null)
try (Locker.Lock lock = _lock.lock())
{
pname=_beans.get(parent);
if (pname==null)
// Is their an object name for the parent
ObjectName pname=null;
if (parent!=null)
{
// create the parent bean
beanAdded(null,parent);
pname=_beans.get(parent);
if (pname==null)
{
// create the parent bean
beanAdded(null,parent);
pname=_beans.get(parent);
}
}
}
// Does an mbean already exist?
if (obj == null || _beans.containsKey(obj))
return;
try
{
// Does an mbean already exist?
if (obj == null || _beans.containsKey(obj))
return;
// Create an MBean for the object
Object mbean = ObjectMBean.mbeanFor(obj);
if (mbean == null)
return;
ObjectName oname = null;
if (mbean instanceof ObjectMBean)
{
@ -171,7 +178,6 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
if (domain == null)
domain = obj.getClass().getPackage().getName();
String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
int dot = type.lastIndexOf('.');
if (dot >= 0)
@ -183,10 +189,10 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
String context = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectContextBasis()):null;
if (context==null && pname!=null)
context=pname.getKeyProperty("context");
if (context != null && context.length()>1)
buf.append("context=").append(context).append(",");
buf.append("type=").append(type);
String name = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectNameBasis()):context;
@ -194,7 +200,7 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
buf.append(",").append("name=").append(name);
String basis = buf.toString();
AtomicInteger count = __unique.get(basis);
if (count==null)
{
@ -202,7 +208,7 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
if (count==null)
count=__unique.get(basis);
}
oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
}
@ -210,7 +216,6 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
if (LOG.isDebugEnabled())
LOG.debug("Registered {}", oinstance.getObjectName());
_beans.put(obj, oinstance.getObjectName());
}
catch (Exception e)
{
@ -223,23 +228,26 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
{
if (LOG.isDebugEnabled())
LOG.debug("beanRemoved {}",obj);
ObjectName bean = _beans.remove(obj);
if (bean != null)
try (Locker.Lock lock = _lock.lock())
{
try
ObjectName bean = _beans.remove(obj);
if (bean != null)
{
_mbeanServer.unregisterMBean(bean);
if (LOG.isDebugEnabled())
LOG.debug("Unregistered {}", bean);
}
catch (javax.management.InstanceNotFoundException e)
{
LOG.ignore(e);
}
catch (Exception e)
{
LOG.warn(e);
try
{
_mbeanServer.unregisterMBean(bean);
if (LOG.isDebugEnabled())
LOG.debug("Unregistered {}", bean);
}
catch (javax.management.InstanceNotFoundException e)
{
LOG.ignore(e);
}
catch (Exception e)
{
LOG.warn(e);
}
}
}
}
@ -258,8 +266,11 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
@Override
public void dump(Appendable out, String indent) throws IOException
{
ContainerLifeCycle.dumpObject(out,this);
ContainerLifeCycle.dump(out, indent, _beans.entrySet());
try (Locker.Lock lock = _lock.lock())
{
ContainerLifeCycle.dumpObject(out,this);
ContainerLifeCycle.dump(out, indent, _beans.entrySet());
}
}
@Override
@ -270,17 +281,22 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
public void destroy()
{
for (ObjectName oname : _beans.values())
if (oname!=null)
try (Locker.Lock lock = _lock.lock())
{
for (ObjectName oname : _beans.values())
{
try
if (oname!=null)
{
_mbeanServer.unregisterMBean(oname);
}
catch (MBeanRegistrationException | InstanceNotFoundException e)
{
LOG.warn(e);
try
{
_mbeanServer.unregisterMBean(oname);
}
catch (MBeanRegistrationException | InstanceNotFoundException e)
{
LOG.warn(e);
}
}
}
}
}
}

View File

@ -71,7 +71,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
* @return true if logging is enabled
*/
protected abstract boolean isEnabled();
/* ------------------------------------------------------------ */
/**
@ -82,7 +82,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
public abstract void write(String requestEntry) throws IOException;
/* ------------------------------------------------------------ */
private void append(StringBuilder buf,String s)
{
if (s==null || s.length()==0)
@ -90,7 +90,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
else
buf.append(s);
}
/**
* Writes the request and response information to the output stream.
*
@ -101,9 +101,6 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
{
try
{
int status = response.getCommittedMetaData().getStatus();
long written = response.getHttpChannel().getBytesWritten();
if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
return;
@ -147,6 +144,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
append(buf,request.getProtocol());
buf.append("\" ");
int status = response.getCommittedMetaData().getStatus();
if (status >=0)
{
buf.append((char)('0' + ((status / 100) % 10)));
@ -156,6 +154,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
else
buf.append(status);
long written = response.getHttpChannel().getBytesWritten();
if (written >= 0)
{
buf.append(' ');
@ -180,7 +179,7 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
if (_extended)
logExtended(request, buf);
logExtended(buf, request, response);
if (_logCookies)
{
@ -221,18 +220,12 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
LOG.warn(e);
}
}
/* ------------------------------------------------------------ */
/**
* Writes extended request and response information to the output stream.
*
* @param request request object
* @param b StringBuilder to write to
* @throws IOException if unable to log the extended information
* @deprecated override {@link #logExtended(StringBuilder, Request, Response)} instead
*/
protected void logExtended(Request request,
StringBuilder b) throws IOException
@Deprecated
protected void logExtended(Request request, StringBuilder b) throws IOException
{
String referer = request.getHeader(HttpHeader.REFERER.toString());
if (referer == null)
@ -255,6 +248,18 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
}
}
/**
* Writes extended request and response information to the output stream.
*
* @param b StringBuilder to write to
* @param request request object
* @param response response object
* @throws IOException if unable to log the extended information
*/
protected void logExtended(StringBuilder b, Request request, Response response) throws IOException
{
logExtended(request, b);
}
/**
* Set request paths that will not be logged.

View File

@ -90,7 +90,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
return last;
}
public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint, HttpParser.Compliance compliance)
{
super(endPoint, connector.getExecutor());
_config = config;
@ -99,7 +99,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_generator = newHttpGenerator();
_channel = newHttpChannel();
_input = _channel.getRequest().getHttpInput();
_parser = newHttpParser();
_parser = newHttpParser(compliance);
if (LOG.isDebugEnabled())
LOG.debug("New HTTP Connection {}", this);
}
@ -119,9 +119,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
return new HttpChannelOverHttp(this, _connector, _config, getEndPoint(), this);
}
protected HttpParser newHttpParser()
protected HttpParser newHttpParser(HttpParser.Compliance compliance)
{
return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize());
return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize(), compliance);
}
protected HttpParser.RequestHandler newRequestHandler()

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.server;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
@ -31,6 +32,7 @@ import org.eclipse.jetty.util.annotation.Name;
public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpConfiguration.ConnectionFactory
{
private final HttpConfiguration _config;
private HttpParser.Compliance _httpCompliance=HttpParser.Compliance.RFC7230;
public HttpConnectionFactory()
{
@ -52,9 +54,24 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements
return _config;
}
public HttpParser.Compliance getHttpCompliance()
{
return _httpCompliance;
}
/**
* @param httpCompliance String value of {@link HttpParser.Compliance}
*/
public void setHttpCompliance(HttpParser.Compliance httpCompliance)
{
_httpCompliance = httpCompliance;
}
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
return configure(new HttpConnection(_config, connector, endPoint), connector, endPoint);
return configure(new HttpConnection(_config, connector, endPoint, _httpCompliance), connector, endPoint);
}
}

View File

@ -29,6 +29,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.Connection;
@ -55,7 +56,7 @@ public class ExtendedServerTest extends HttpServerTestBase
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
return configure(new ExtendedHttpConnection(getHttpConfiguration(), connector, endPoint), connector, endPoint);
return configure(new ExtendedHttpConnection(getHttpConfiguration(), connector, endPoint,getHttpCompliance()), connector, endPoint);
}
})
{
@ -93,9 +94,9 @@ public class ExtendedServerTest extends HttpServerTestBase
private static class ExtendedHttpConnection extends HttpConnection
{
public ExtendedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
public ExtendedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint, HttpParser.Compliance compliance)
{
super(config,connector,endPoint);
super(config,connector,endPoint,compliance);
}
@Override

View File

@ -54,7 +54,7 @@ public class SlowClientWithPipelinedRequestTest
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
return configure(new HttpConnection(new HttpConfiguration(),connector,endPoint)
return configure(new HttpConnection(getHttpConfiguration(),connector,endPoint,getHttpCompliance())
{
@Override
public void onFillable()