Merge remote-tracking branch 'origin/master' into jetty-9.4.x-Feature

This commit is contained in:
Greg Wilkins 2016-02-23 14:35:31 +01:00
commit 5263f15acd
49 changed files with 1100 additions and 605 deletions

View File

@ -44,12 +44,12 @@ Contact the project developers via the project's "dev" list.
Search for bugs
----------------
This project uses Bugzilla to track ongoing development and issues.
This project uses GitHub Issues to track ongoing development and issues.
- [https://bugs.eclipse.org/bugs/buglist.cgi?product=Jetty](https://bugs.eclipse.org/bugs/buglist.cgi?product=Jetty)
- [https://github.com/eclipse/jetty.project/issues](https://github.com/eclipse/jetty.project/issues)
Create a new bug
-----------------
Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
- [https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Jetty](https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Jetty)
- [https://github.com/eclipse/jetty.project/issues](https://github.com/eclipse/jetty.project/issues)

View File

@ -1,11 +1,21 @@
Eclipse Jetty Canonical Repository
==================================
This is the canonical repository for the Jetty project, feel free to fork and contribute now!
Build Status
------------
- Master Branch - [![Build Status](http://ci.webtide.net:9099/build/job/jetty-master/badge/icon)](http://ci.webtide.net:9099/build/job/jetty-master/)
- Jetty 9.3.x Branch - [![Build Status](http://ci.webtide.net:9099/build/job/jetty-9.3.x/badge/icon)](http://ci.webtide.net:9099/build/job/jetty-9.3.x/)
- Jetty 9.2.x Branch - [![Build Status](http://ci.webtide.net:9099/build/job/jetty-9.2.x/badge/icon)](http://ci.webtide.net:9099/build/job/jetty-9.2.x/)
Make sure you have a CLA on file!
- [https://www.eclipse.org/legal/clafaq.php](https://www.eclipse.org/legal/clafaq.php)
Project description
============
-------------------
Jetty is a lightweight highly scalable java based web server and servlet engine.
Our goal is to support web protocols like HTTP, HTTP/2 and WebSocket in a high
@ -18,13 +28,13 @@ distribution for webapp deployment.
- [https://projects.eclipse.org/projects/rt.jetty](https://projects.eclipse.org/projects/rt.jetty)
Documentation
============
-------------
Project documentation is located on our Eclipse website.
- [http://www.eclipse.org/jetty/documentation](http://www.eclipse.org/jetty/documentation)
Professional Services
============
---------------------
Expert advice and production support are available through [http://webtide.com](Webtide.com).

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

@ -31,14 +31,17 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.
* <p>
* It is possible to specify, at the constructor, a buffer size used to read content from the
* stream, by default 4096 bytes.
* <p>A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.</p>
* <p>It is possible to specify, at the constructor, a buffer size used to read
* content from the stream, by default 4096 bytes.
* If a {@link ByteBufferPool} is provided via {@link #setByteBufferPool(ByteBufferPool)},
* the buffer will be allocated from that pool, otherwise one buffer will be
* allocated and used to read the file.</p>
*/
public class PathContentProvider extends AbstractTypedContentProvider
{
@ -47,6 +50,7 @@ public class PathContentProvider extends AbstractTypedContentProvider
private final Path filePath;
private final long fileSize;
private final int bufferSize;
private ByteBufferPool bufferPool;
public PathContentProvider(Path filePath) throws IOException
{
@ -81,6 +85,16 @@ public class PathContentProvider extends AbstractTypedContentProvider
return fileSize;
}
public ByteBufferPool getByteBufferPool()
{
return bufferPool;
}
public void setByteBufferPool(ByteBufferPool byteBufferPool)
{
this.bufferPool = byteBufferPool;
}
@Override
public Iterator<ByteBuffer> iterator()
{
@ -89,7 +103,7 @@ public class PathContentProvider extends AbstractTypedContentProvider
private class PathIterator implements Iterator<ByteBuffer>, Closeable
{
private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
private ByteBuffer buffer;
private SeekableByteChannel channel;
private long position;
@ -106,6 +120,9 @@ public class PathContentProvider extends AbstractTypedContentProvider
{
if (channel == null)
{
buffer = bufferPool == null ?
ByteBuffer.allocateDirect(bufferSize) :
bufferPool.acquire(bufferSize, true);
channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
if (LOG.isDebugEnabled())
LOG.debug("Opened file {}", filePath);
@ -121,9 +138,6 @@ public class PathContentProvider extends AbstractTypedContentProvider
position += read;
if (!hasNext())
close();
buffer.flip();
return buffer;
}
@ -139,17 +153,13 @@ public class PathContentProvider extends AbstractTypedContentProvider
}
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
@Override
public void close()
{
try
{
if (bufferPool != null && buffer != null)
bufferPool.release(buffer);
if (channel != null)
channel.close();
}

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

@ -43,7 +43,6 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@ -284,7 +283,8 @@ public class MultiPartContentProviderTest extends AbstractHttpClientServerTest
});
MultiPartContentProvider multiPart = new MultiPartContentProvider();
ContentProvider content = new PathContentProvider(contentType, tmpPath);
PathContentProvider content = new PathContentProvider(contentType, tmpPath);
content.setByteBufferPool(client.getByteBufferPool());
multiPart.addFilePart(name, tmpPath.getFileName().toString(), content, null);
multiPart.close();
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())

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

@ -41,7 +41,6 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
/**
@ -51,8 +50,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
private SessionIdManager _idMgr = null;
private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
private int _infinispanIdleTimeoutSec;
/**
@ -139,21 +137,25 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
long now = System.currentTimeMillis();
Set<String> expired = new HashSet<String>();
if (LOG.isDebugEnabled())
LOG.debug("Getting expired sessions " + now);
for (String candidate:candidates)
{
if (LOG.isDebugEnabled())
LOG.debug("Checking expiry for candidate {}", candidate);
try
{
SessionData sd = load(candidate);
if (sd == null || sd.isExpiredAt(now))
if (sd == null || sd.isExpiredAt(now))
{
expired.add(candidate);
if (LOG.isDebugEnabled())
LOG.debug("Is null {} is expired {}", (sd==null), (sd !=null));
}
}
catch (Exception e)
{
LOG.warn("Error checking if session {} is expired", candidate, e);
LOG.warn("Error checking if candidate {} is expired", candidate, e);
}
}
@ -170,10 +172,13 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
//if no requests arrive at any node before this timeout occurs, or no node
//scavenges the session before this timeout occurs, the session will be removed.
//NOTE: that no session listeners can be called for this.
if (data.getMaxInactiveMs() > 0)
_cache.put(getCacheKey(id, _context), data, -1, TimeUnit.MILLISECONDS, (data.getMaxInactiveMs() * _idleExpiryMultiple), TimeUnit.MILLISECONDS);
if (data.getMaxInactiveMs() > 0 && getInfinispanIdleTimeoutSec() > 0)
_cache.put(getCacheKey(id, _context), data, -1, TimeUnit.MILLISECONDS, getInfinispanIdleTimeoutSec(), TimeUnit.SECONDS);
else
_cache.put(getCacheKey(id, _context), data);
if (LOG.isDebugEnabled())
LOG.debug("Session {} saved to infinispan, expires {} ", id, data.getExpiry());
//tickle the session id manager to keep the sessionid entry for this session up-to-date
if (_idMgr != null && _idMgr instanceof InfinispanSessionIdManager)
@ -198,4 +203,14 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
{
return true;
}
public void setInfinispanIdleTimeoutSec (int sec)
{
_infinispanIdleTimeoutSec = sec;
}
public int getInfinispanIdleTimeoutSec ()
{
return _infinispanIdleTimeoutSec;
}
}

View File

@ -26,11 +26,12 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.session.AbstractSessionIdManager;
import org.eclipse.jetty.server.session.Session;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.server.session.SessionManager;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.infinispan.commons.api.BasicCache;
@ -68,9 +69,8 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public final static String ID_KEY = "__o.e.j.s.infinispanIdMgr__";
public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
protected BasicCache<String,Object> _cache;
private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
private int _infinispanIdleTimeoutSec = 0;
@ -141,22 +141,27 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
LOG.warn("Problem checking inUse for id="+clusterId, e);
return false;
}
}
public void setInfinispanIdleTimeoutSec (int sec)
{
if (sec <= 1)
{
LOG.warn("Idle expiry multiple of {} for session ids set to less than minimum. Using value of {} instead.", sec, 0);
_infinispanIdleTimeoutSec = 0;
}
else
_infinispanIdleTimeoutSec = sec;
}
public void setIdleExpiryMultiple (int multiplier)
public int getInfinispanIdleTimeoutSec()
{
if (multiplier <= 1)
{
LOG.warn("Idle expiry multiple of {} for session ids set to less than minimum. Using value of {} instead.", multiplier, DEFAULT_IDLE_EXPIRY_MULTIPLE);
}
_idleExpiryMultiple = multiplier;
}
public int getIdleExpiryMultiple ()
{
return _idleExpiryMultiple;
return _infinispanIdleTimeoutSec;
}
@ -273,10 +278,10 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
if (session == null)
return;
if (session.getMaxInactiveInterval() == 0)
insert (session.getId());
if (session.getMaxInactiveInterval() > 0 && getInfinispanIdleTimeoutSec() > 0)
insert (session.getId(), getInfinispanIdleTimeoutSec());
else
insert (session.getId(), session.getMaxInactiveInterval() * getIdleExpiryMultiple());
insert (session.getId());
}
/**
@ -287,4 +292,25 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
{
return delete (id);
}
/* ------------------------------------------------------------ */
/**
* Remove an id from use by telling all contexts to remove a session with this id.
*
* @see org.eclipse.jetty.server.SessionIdManager#expireAll(java.lang.String)
*/
@Override
public void expireAll(String id)
{
LOG.debug("Expiring "+id);
//take the id out of the list of known sessionids for this node - TODO consider if we didn't remove it from this node
//it is because infinispan probably already timed it out. So, we only want to expire it from memory and NOT load it if present
removeId(id);
//tell all contexts that may have a session object with this id to
//get rid of them
for (SessionManager manager:getSessionManagers())
{
manager.invalidate(id);
}
}
}

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

@ -36,7 +36,6 @@ import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.WriteFlusher;
import org.eclipse.jetty.util.BufferUtil;
@ -144,18 +143,8 @@ public class SslConnection extends AbstractConnection
@Override
public void onOpen()
{
try
{
// Begin the handshake
_sslEngine.beginHandshake();
super.onOpen();
getDecryptedEndPoint().getConnection().onOpen();
}
catch (SSLException x)
{
getEndPoint().close();
throw new RuntimeIOException(x);
}
super.onOpen();
getDecryptedEndPoint().getConnection().onOpen();
}
@Override

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.jmx;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
@ -29,7 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.eclipse.jetty.util.component.Container;
@ -44,15 +42,15 @@ import org.eclipse.jetty.util.log.Logger;
public class MBeanContainer implements Container.InheritedListener, Dumpable
{
private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<String, AtomicInteger>();
private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<>();
public static void resetUnique()
{
__unique.clear();
}
private final MBeanServer _mbeanServer;
private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
private final Map<Object, ObjectName> _beans = new ConcurrentHashMap<>();
private String _domain = null;
/**
@ -61,25 +59,23 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
* @param object instance for which object name is looked up
* @return object name associated with specified instance, or null if not found
*/
public synchronized ObjectName findMBean(Object object)
public ObjectName findMBean(Object object)
{
ObjectName bean = _beans.get(object);
return bean == null ? null : bean;
return _beans.get(object);
}
/**
* Lookup an instance by object name
*
* @param oname object name of instance
* @param objectName object name of instance
* @return instance associated with specified object name, or null if not found
*/
public synchronized Object findBean(ObjectName oname)
public Object findBean(ObjectName objectName)
{
for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
{
ObjectName bean = entry.getValue();
if (bean.equals(oname))
return entry.getKey();
if (entry.getKey().equals(objectName))
return entry.getValue();
}
return null;
}
@ -129,92 +125,90 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
public void beanAdded(Container parent, Object obj)
{
if (LOG.isDebugEnabled())
LOG.debug("beanAdded {}->{}",parent,obj);
// Is their an object name for the parent
ObjectName pname=null;
if (parent!=null)
LOG.debug("beanAdded {}->{}", parent, obj);
// Is there an object name for the parent ?
ObjectName parentObjectName = null;
if (parent != null)
{
pname=_beans.get(parent);
if (pname==null)
parentObjectName = findMBean(parent);
if (parentObjectName == null)
{
// create the parent bean
beanAdded(null,parent);
pname=_beans.get(parent);
// Create the parent bean.
beanAdded(null, parent);
parentObjectName = findMBean(parent);
}
}
// Does an mbean already exist?
// Does the mbean already exist ?
if (obj == null || _beans.containsKey(obj))
return;
try
{
// Create an MBean for the object
// Create an MBean for the object.
Object mbean = ObjectMBean.mbeanFor(obj);
if (mbean == null)
return;
ObjectName oname = null;
ObjectName objectName = null;
if (mbean instanceof ObjectMBean)
{
((ObjectMBean)mbean).setMBeanContainer(this);
oname = ((ObjectMBean)mbean).getObjectName();
objectName = ((ObjectMBean)mbean).getObjectName();
}
//no override mbean object name, so make a generic one
if (oname == null)
{
//if no explicit domain, create one
// No override of the mbean's ObjectName, so make a generic one.
if (objectName == null)
{
// If no explicit domain, create one.
String domain = _domain;
if (domain == null)
domain = obj.getClass().getPackage().getName();
String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
int dot = type.lastIndexOf('.');
if (dot >= 0)
type = type.substring(dot + 1);
StringBuilder buf = new StringBuilder();
StringBuffer buf = new StringBuffer();
String context = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectContextBasis()) : null;
if (context == null && parentObjectName != null)
context = parentObjectName.getKeyProperty("context");
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)
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;
if (name != null && name.length()>1)
String name = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectNameBasis()) : context;
if (name != null && name.length() > 1)
buf.append(",").append("name=").append(name);
String basis = buf.toString();
AtomicInteger count = __unique.get(basis);
if (count==null)
if (count == null)
{
count=__unique.putIfAbsent(basis,new AtomicInteger());
if (count==null)
count=__unique.get(basis);
count = new AtomicInteger();
AtomicInteger existing = __unique.putIfAbsent(basis, count);
if (existing != null)
count = existing;
}
oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
objectName = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
}
ObjectInstance oinstance = _mbeanServer.registerMBean(mbean, oname);
_mbeanServer.registerMBean(mbean, objectName);
if (LOG.isDebugEnabled())
LOG.debug("Registered {}", oinstance.getObjectName());
_beans.put(obj, oinstance.getObjectName());
LOG.debug("Registered {}", objectName);
_beans.put(obj, objectName);
}
catch (Exception e)
catch (Throwable x)
{
LOG.warn("bean: " + obj, e);
LOG.warn("bean: " + obj, x);
}
}
@ -222,26 +216,12 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
public void beanRemoved(Container parent, Object obj)
{
if (LOG.isDebugEnabled())
LOG.debug("beanRemoved {}",obj);
ObjectName bean = _beans.remove(obj);
LOG.debug("beanRemoved {}", obj);
if (bean != null)
{
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);
}
}
ObjectName objectName = _beans.remove(obj);
if (objectName != null)
unregister(objectName);
}
/**
@ -250,9 +230,15 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
*/
public String makeName(String basis)
{
if (basis==null)
return basis;
return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
if (basis == null)
return null;
return basis
.replace(':', '_')
.replace('*', '_')
.replace('?', '_')
.replace('=', '_')
.replace(',', '_')
.replace(' ', '_');
}
@Override
@ -270,17 +256,26 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable
public void destroy()
{
for (ObjectName oname : _beans.values())
if (oname!=null)
{
try
{
_mbeanServer.unregisterMBean(oname);
}
catch (MBeanRegistrationException | InstanceNotFoundException e)
{
LOG.warn(e);
}
}
_beans.values().stream()
.filter(objectName -> objectName != null)
.forEach(this::unregister);
}
private void unregister(ObjectName objectName)
{
try
{
getMBeanServer().unregisterMBean(objectName);
if (LOG.isDebugEnabled())
LOG.debug("Unregistered {}", objectName);
}
catch (MBeanRegistrationException | InstanceNotFoundException x)
{
LOG.ignore(x);
}
catch (Throwable x)
{
LOG.warn(x);
}
}
}

View File

@ -33,7 +33,6 @@
<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
<Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.sslContext.keyStorePath" default="etc/keystore"/></Set>
<Set name="KeyStorePassword"><Property name="jetty.sslContext.keyStorePassword" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
<Set name="KeyManagerPassword"><Property name="jetty.sslContext.keyManagerPassword" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
<Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.sslContext.trustStorePath" default="etc/keystore"/></Set>
<Set name="TrustStorePassword"><Property name="jetty.sslContext.trustStorePassword" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
<Set name="EndpointIdentificationAlgorithm"></Set>

View File

@ -530,6 +530,34 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
return getConnectionFactory(_defaultProtocol);
}
protected boolean handleAcceptFailure(Throwable previous, Throwable current)
{
if (isAccepting())
{
if (previous == null)
LOG.warn(current);
else
LOG.debug(current);
}
else
{
LOG.ignore(current);
}
try
{
// Arbitrary sleep to avoid spin looping.
// Subclasses may decide for a different
// sleep policy or closing the connector.
Thread.sleep(1000);
return true;
}
catch (Throwable x)
{
return false;
}
}
private class Acceptor implements Runnable
{
private final int _id;
@ -559,18 +587,20 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
try
{
Throwable exception = null;
while (isAccepting())
{
try
{
accept(_id);
exception = null;
}
catch (Throwable e)
catch (Throwable x)
{
if (isAccepting())
LOG.warn(e);
if (handleAcceptFailure(exception, x))
exception = x;
else
LOG.ignore(e);
break;
}
}
}

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

@ -470,7 +470,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
}
else
{
LOG.info(_request.getRequestURI(), failure);
LOG.warn(_request.getRequestURI(), failure);
}
try

View File

@ -518,7 +518,8 @@ public class HttpChannelState
}
catch(Throwable x)
{
LOG.debug("Exception while invoking listener " + listener,x);
LOG.warn(x+" while invoking onTimeout listener " + listener);
LOG.debug(x);
if (error.get()==null)
error.set(x);
else
@ -721,7 +722,8 @@ public class HttpChannelState
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener,x);
LOG.warn(x+" while invoking onError listener " + listener);
LOG.debug(x);
}
}
}
@ -815,7 +817,8 @@ public class HttpChannelState
}
catch(Exception e)
{
LOG.warn("Exception while invoking listener " + listener,e);
LOG.warn(e+" while invoking onComplete listener " + listener);
LOG.debug(e);
}
}
}

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

@ -207,7 +207,7 @@ public class Request implements HttpServletRequest
/* ------------------------------------------------------------ */
public HttpFields getHttpFields()
{
return _metadata.getFields();
return _metadata==null?null:_metadata.getFields();
}
/* ------------------------------------------------------------ */
@ -369,7 +369,7 @@ public class Request implements HttpServletRequest
/* ------------------------------------------------------------ */
private void extractQueryParameters()
{
if (_metadata.getURI() == null || !_metadata.getURI().hasQuery())
if (_metadata == null || _metadata.getURI() == null || !_metadata.getURI().hasQuery())
_queryParameters=NO_PARAMS;
else
{
@ -656,7 +656,7 @@ public class Request implements HttpServletRequest
@Override
public String getContentType()
{
String content_type = _metadata.getFields().get(HttpHeader.CONTENT_TYPE);
String content_type = _metadata==null?null:_metadata.getFields().get(HttpHeader.CONTENT_TYPE);
if (_characterEncoding==null && content_type!=null)
{
MimeTypes.Type mime = MimeTypes.CACHE.get(content_type);
@ -944,7 +944,7 @@ public class Request implements HttpServletRequest
if (local!=null)
return local.getHostString();
}
try
{
String name =InetAddress.getLocalHost().getHostName();
@ -1142,7 +1142,7 @@ public class Request implements HttpServletRequest
@Override
public String getQueryString()
{
return _metadata.getURI().getQuery();
return _metadata==null?null:_metadata.getURI().getQuery();
}
/* ------------------------------------------------------------ */
@ -1370,7 +1370,7 @@ public class Request implements HttpServletRequest
@Override
public String getServerName()
{
String name = _metadata.getURI().getHost();
String name = _metadata==null?null:_metadata.getURI().getHost();
// Return already determined host
if (name != null)
@ -1383,7 +1383,7 @@ public class Request implements HttpServletRequest
private String findServerName()
{
// Return host from header field
HttpField host = _metadata.getFields().getField(HttpHeader.HOST);
HttpField host = _metadata==null?null:_metadata.getFields().getField(HttpHeader.HOST);
if (host!=null)
{
// TODO is this needed now?
@ -1418,8 +1418,8 @@ public class Request implements HttpServletRequest
@Override
public int getServerPort()
{
HttpURI uri = _metadata.getURI();
int port = (uri.getHost()==null)?findServerPort():uri.getPort();
HttpURI uri = _metadata==null?null:_metadata.getURI();
int port = (uri == null || uri.getHost()==null)?findServerPort():uri.getPort();
// If no port specified, return the default port for the scheme
if (port <= 0)
@ -1437,7 +1437,7 @@ public class Request implements HttpServletRequest
private int findServerPort()
{
// Return host from header field
HttpField host = _metadata.getFields().getField(HttpHeader.HOST);
HttpField host = _metadata==null?null:_metadata.getFields().getField(HttpHeader.HOST);
if (host!=null)
{
// TODO is this needed now?

View File

@ -564,12 +564,15 @@ public class Response implements HttpServletResponse
{
ContextHandler.Context context = request.getContext();
ContextHandler contextHandler = context == null ? _channel.getState().getContextHandler() : context.getContextHandler();
ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(), contextHandler);
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, code);
request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, request.getServletName());
error_handler.handle(null, request, request, this);
ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(), contextHandler);
if (error_handler!=null)
error_handler.handle(null, request, request, this);
else
_out.close();
}
}

View File

@ -46,6 +46,7 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.Attributes;
@ -86,6 +87,7 @@ public class Server extends HandlerWrapper implements Attributes
private boolean _stopAtShutdown;
private boolean _dumpAfterStart=false;
private boolean _dumpBeforeStop=false;
private ErrorHandler _errorHandler;
private RequestLog _requestLog;
private final Locker _dateLocker = new Locker();
@ -142,6 +144,12 @@ public class Server extends HandlerWrapper implements Attributes
return _requestLog;
}
/* ------------------------------------------------------------ */
public ErrorHandler getErrorHandler()
{
return _errorHandler;
}
/* ------------------------------------------------------------ */
public void setRequestLog(RequestLog requestLog)
{
@ -149,6 +157,17 @@ public class Server extends HandlerWrapper implements Attributes
_requestLog = requestLog;
}
/* ------------------------------------------------------------ */
public void setErrorHandler(ErrorHandler errorHandler)
{
if (errorHandler instanceof ErrorHandler.ErrorPageMapper)
throw new IllegalArgumentException("ErrorPageMapper is applicable only to ContextHandler");
updateBean(_errorHandler,errorHandler);
_errorHandler=errorHandler;
if (errorHandler!=null)
errorHandler.setServer(this);
}
/* ------------------------------------------------------------ */
@ManagedAttribute("version of this server")
public static String getVersion()
@ -162,7 +181,6 @@ public class Server extends HandlerWrapper implements Attributes
return _stopAtShutdown;
}
/* ------------------------------------------------------------ */
/**
* Set a graceful stop time.
@ -330,6 +348,14 @@ public class Server extends HandlerWrapper implements Attributes
@Override
protected void doStart() throws Exception
{
// Create an error handler if there is none
if (_errorHandler==null)
_errorHandler=getBean(ErrorHandler.class);
if (_errorHandler==null)
setErrorHandler(new ErrorHandler());
if (_errorHandler instanceof ErrorHandler.ErrorPageMapper)
LOG.warn("ErrorPageMapper not supported for Server level Error Handling");
//If the Server should be stopped when the jvm exits, register
//with the shutdown handler thread.
if (getStopAtShutdown())

View File

@ -48,7 +48,7 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** Handler for Error pages
* An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or
* {@link org.eclipse.jetty.server.Server#addBean(Object)}.
* {@link Server#setErrorHandler(ErrorHandler).
* It is called by the HttpResponse.sendError method to write a error page via {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
* or via {@link #badMessageError(int, String, HttpFields)} for bad requests for which a dispatch cannot be done.
*
@ -62,6 +62,11 @@ public class ErrorHandler extends AbstractHandler
boolean _showMessageInTitle=true;
String _cacheControl="must-revalidate,no-cache,no-store";
/* ------------------------------------------------------------ */
public ErrorHandler()
{
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.jetty.server.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
@ -79,22 +84,35 @@ public class ErrorHandler extends AbstractHandler
if (this instanceof ErrorPageMapper)
{
String error_page=((ErrorPageMapper)this).getErrorPage(request);
if (error_page!=null && request.getServletContext()!=null)
if (error_page!=null)
{
String old_error_page=(String)request.getAttribute(ERROR_PAGE);
if (old_error_page==null || !old_error_page.equals(error_page))
ServletContext servlet_context = request.getServletContext();
if (servlet_context==null)
servlet_context=ContextHandler.getCurrentContext();
if (servlet_context==null)
{
LOG.warn("No ServletContext for error page {}",error_page);
}
else if (old_error_page!=null && old_error_page.equals(error_page))
{
LOG.warn("Error page loop {}",error_page);
}
else
{
request.setAttribute(ERROR_PAGE, error_page);
Dispatcher dispatcher = (Dispatcher) request.getServletContext().getRequestDispatcher(error_page);
Dispatcher dispatcher = (Dispatcher) servlet_context.getRequestDispatcher(error_page);
try
{
if (LOG.isDebugEnabled())
LOG.debug("error page dispatch {}->{}",error_page,dispatcher);
if(dispatcher!=null)
{
dispatcher.error(request, response);
return;
}
LOG.warn("No error page "+error_page);
LOG.warn("No error page found "+error_page);
}
catch (ServletException e)
{
@ -102,7 +120,9 @@ public class ErrorHandler extends AbstractHandler
return;
}
}
} else {
}
else
{
if (LOG.isDebugEnabled())
{
LOG.debug("No Error Page mapping for request({} {}) (using default)",request.getMethod(),request.getRequestURI());
@ -110,12 +130,15 @@ public class ErrorHandler extends AbstractHandler
}
}
baseRequest.setHandled(true);
response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());
if (_cacheControl!=null)
response.setHeader(HttpHeader.CACHE_CONTROL.asString(), _cacheControl);
ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(4096);
String reason=(response instanceof Response)?((Response)response).getReason():null;
if (LOG.isDebugEnabled())
LOG.debug("default error page {} {}",request,response);
handleErrorPage(request, writer, response.getStatus(), reason);
writer.flush();
response.setContentLength(writer.size());

View File

@ -348,7 +348,18 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
@Override
public void expireAll(String id)
{
//take the id out of the list of known sessionids for this node
if (LOG.isDebugEnabled())
LOG.debug("Expiring {}",id);
//TODO handle cases:
//1. infinispan session id manager may not be able to remove id because it has timed out in infinispan but yet
//we want to remove a session object from the session store (session data store probably ok because it has same timeout as session id mgr entries)
//2. a session id manager may not know all session ids (ie subset in memory only) and therefore won't remove
//it, but it should be removed from the session data store (could it be in session store?)
//3. old sessions that no node is handling, eg after all restarted, but need to be removed from
//session data store, AND have listeners called on them.
//BUT want to avoid loading into memory sessions that this node is not managing (eg have 3 nodes all running session mgrs,
//all 3 find the expired session and load it into memory and expire it
if (removeId(id))
{
//tell all contexts that may have a session object with this id to
@ -358,6 +369,8 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
manager.invalidate(id);
}
}
else if (LOG.isDebugEnabled())
LOG.debug("Not present in idmgr: {}", id);
}
/* ------------------------------------------------------------ */

View File

@ -867,11 +867,11 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
connection.setAutoCommit(true);
/*
* 1. Select sessions for our node and context that have expired
* 1. Select sessions for our context that have expired
*/
long upperBound = now;
if (LOG.isDebugEnabled())
LOG.debug ("{}- Pass 1: Searching for sessions for node {} and context {} expired before {}", _context.getWorkerName(), _context.getCanonicalContextPath(), upperBound);
LOG.debug ("{}- Pass 1: Searching for sessions for context {} expired before {}", _context.getWorkerName(), _context.getCanonicalContextPath(), upperBound);
try (PreparedStatement statement = _sessionTableSchema.getMyExpiredSessionsStatement(connection, _context.getCanonicalContextPath(), _context.getVhost(), upperBound))
{

View File

@ -26,6 +26,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* SessionData
*
@ -36,7 +39,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class SessionData implements Serializable
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
private static final long serialVersionUID = 1L;
@ -285,6 +288,8 @@ public class SessionData implements Serializable
public boolean isExpiredAt (long time)
{
if (LOG.isDebugEnabled())
LOG.debug("Testing expiry on session {}: Never expires? {} Is expired?{}", _id, (getExpiry()<= 0), (getExpiry() < time));
if (getExpiry() <= 0)
return false; //never expires

View File

@ -581,7 +581,6 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
session.setSessionManager(this);
session.setLastNode(_sessionIdManager.getWorkerName());
session.getSessionData().setExpiry(_dftMaxIdleSecs <= 0 ? 0 : (created + _dftMaxIdleSecs*1000L));
if (request.isSecure())
session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
@ -822,6 +821,8 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
}
}
}
//TODO if session object is not known to this node, how to get rid of it if no other
//node knows about it?
return session;
}

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

@ -22,7 +22,9 @@ import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
@ -84,8 +86,8 @@ public class LocalAsyncContextTest
_handler.setCompleteAfter(-1);
response=process(null);
check(response,"TIMEOUT");
assertEquals(1,__completed.get());
assertEquals(1,__completed1.get());
spinAssertEquals(1,__completed::get);
spinAssertEquals(1,__completed1::get);
}
@Test
@ -218,8 +220,8 @@ public class LocalAsyncContextTest
_handler.setCompleteAfter2(-1);
response=process(null);
check(response,"STARTASYNC","DISPATCHED","startasync","STARTASYNC","DISPATCHED");
assertEquals(1,__completed.get());
assertEquals(0,__completed1.get());
spinAssertEquals(1,__completed::get);
spinAssertEquals(0,__completed1::get);
}
@ -596,4 +598,33 @@ public class LocalAsyncContextTest
}
};
static <T> void spinAssertEquals(T expected, Supplier<T> actualSupplier)
{
spinAssertEquals(expected,actualSupplier,10,TimeUnit.SECONDS);
}
static <T> void spinAssertEquals(T expected, Supplier<T> actualSupplier, long waitFor, TimeUnit units)
{
long now = System.nanoTime();
long end = now+units.toNanos(waitFor);
T actual=null;
while(now < end)
{
actual=actualSupplier.get();
if (actual==null && expected==null ||
actual!=null && actual.equals(expected))
break;
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
now = System.nanoTime();
}
assertEquals(expected,actual);
}
}

View File

@ -55,6 +55,7 @@ import javax.servlet.http.Part;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.Utf8Appendable;
@ -217,7 +218,7 @@ public class RequestTest
@Test
public void testMultiPart() throws Exception
{
{
final File testTmpDir = File.createTempFile("reqtest", null);
if (testTmpDir.exists())
testTmpDir.delete();
@ -275,10 +276,10 @@ public class RequestTest
// System.err.println(responses);
assertTrue(responses.startsWith("HTTP/1.1 200"));
}
@Test
public void testBadMultiPart() throws Exception
{
{
//a bad multipart where one of the fields has no name
final File testTmpDir = File.createTempFile("badmptest", null);
if (testTmpDir.exists())
@ -480,7 +481,7 @@ public class RequestTest
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
response=_connector.getResponses(
@ -492,7 +493,7 @@ public class RequestTest
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
response=_connector.getResponses(
"GET http://myhost:8888/ HTTP/1.1\n"+
@ -718,7 +719,7 @@ public class RequestTest
String response = _connector.getResponses(request);
assertThat(response,Matchers.containsString(" 200 OK"));
}
@Test
public void test8859EncodedForm() throws Exception
{
@ -746,7 +747,7 @@ public class RequestTest
String response = _connector.getResponses(request);
assertThat(response,Matchers.containsString(" 200 OK"));
}
@Test
public void testUTF8EncodedForm() throws Exception
{
@ -774,8 +775,8 @@ public class RequestTest
String response = _connector.getResponses(request);
assertThat(response,Matchers.containsString(" 200 OK"));
}
@Test
public void testPartialRead() throws Exception
{
@ -859,10 +860,10 @@ public class RequestTest
assertTrue(responses.indexOf("read='param=wrong' param=right")>0);
}
@Test
public void testSessionAfterRedirect() throws Exception
{
{
Handler handler = new AbstractHandler()
{
@Override
@ -1345,7 +1346,7 @@ public class RequestTest
((StdErrLog)Log.getLogger(HttpChannel.class)).setHideStacks(false);
}
}
@Test
public void testHashDOSSize() throws Exception
{
@ -1399,6 +1400,40 @@ public class RequestTest
request.setCharacterEncoding("doesNotExist");
}
@Test
public void testGetterSafeFromNullPointerException()
{
Request request = new Request(null, null);
assertNull(request.getAuthType());
assertNull(request.getAuthentication());
assertNull(request.getContentType());
assertNull(request.getCookies());
assertNull(request.getContext());
assertNull(request.getContextPath());
assertNull(request.getHttpFields());
assertNull(request.getHttpURI());
assertNotNull(request.getScheme());
assertNotNull(request.getServerName());
assertNotNull(request.getServerPort());
assertNotNull(request.getAttributeNames());
assertFalse(request.getAttributeNames().hasMoreElements());
request.extractParameters();
assertNull(request.getQueryString());
assertNotNull(request.getQueryParameters());
assertEquals(0,request.getQueryParameters().size());
assertNotNull(request.getParameterMap());
assertEquals(0,request.getParameterMap().size());
}
interface RequestTester
{
boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException;
@ -1467,7 +1502,7 @@ public class RequestTest
}
}
}
private class BadMultiPartRequestHandler extends AbstractHandler
{
File tmpDir;
@ -1489,16 +1524,13 @@ public class RequestTest
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
//We should get an error when we getParams if there was a problem parsing the multipart
request.getParameter("xxx");
request.getPart("xxx");
//A 200 response is actually wrong here
}
catch (RuntimeException e)
{
response.sendError(500);
}
}
}
}

View File

@ -53,6 +53,7 @@ import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.eclipse.jetty.server.session.Session;
@ -438,6 +439,37 @@ public class ResponseTest
assertEquals("Super Nanny", response.getReason());
assertEquals("must-revalidate,no-cache,no-store", response.getHeader(HttpHeader.CACHE_CONTROL.asString()));
}
@Test
public void testStatusCodesNoErrorHandler() throws Exception
{
_server.removeBean(_server.getBean(ErrorHandler.class));
Response response = newResponse();
response.sendError(404);
assertEquals(404, response.getStatus());
assertEquals("Not Found", response.getReason());
response = newResponse();
response.sendError(500, "Database Error");
assertEquals(500, response.getStatus());
assertEquals("Database Error", response.getReason());
assertThat(response.getHeader(HttpHeader.CACHE_CONTROL.asString()),Matchers.nullValue());
response = newResponse();
response.setStatus(200);
assertEquals(200, response.getStatus());
assertEquals(null, response.getReason());
response = newResponse();
response.sendError(406, "Super Nanny");
assertEquals(406, response.getStatus());
assertEquals("Super Nanny", response.getReason());
assertThat(response.getHeader(HttpHeader.CACHE_CONTROL.asString()),Matchers.nullValue());
}
@Test
public void testWriteRuntimeIOException() throws Exception

View File

@ -37,6 +37,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -49,6 +50,7 @@ import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers;
import org.junit.Test;
public class ServerConnectorTest
@ -235,4 +237,32 @@ public class ServerConnectorTest
assertEquals(2, connector.getBeans(ConnectionFactory.class).size());
assertEquals(proxy.getProtocol(), connector.getDefaultProtocol());
}
@Test
public void testExceptionWhileAccepting() throws Exception
{
Server server = new Server();
AtomicLong spins = new AtomicLong();
ServerConnector connector = new ServerConnector(server)
{
@Override
public void accept(int acceptorID) throws IOException
{
spins.incrementAndGet();
throw new IOException("explicitly_thrown_by_test");
}
};
server.addConnector(connector);
server.start();
try
{
Thread.sleep(1000);
assertThat(spins.get(), Matchers.lessThan(5L));
}
finally
{
server.stop();
}
}
}

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()

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.servlet;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.TimeUnit;
import javax.servlet.AsyncContext;
@ -31,8 +32,11 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.QuietServletException;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.junit.After;
import org.junit.Test;
@ -82,6 +86,8 @@ public class AsyncListenerTest
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
ServletOutputStream output = response.getOutputStream();
output.println(event.getThrowable().getClass().getName());
if (event.getThrowable().getCause()!=null)
output.println(event.getThrowable().getCause().getClass().getName());
output.println("COMPLETE");
event.getAsyncContext().complete();
});
@ -151,10 +157,17 @@ public class AsyncListenerTest
});
// Add a custom error page.
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
errorHandler.setServer(server);
errorHandler.addErrorPage(HttpStatus.BAD_GATEWAY_502, "/error");
server.addManaged(errorHandler);
ErrorHandler errorHandler = new ErrorHandler()
{
@Override
protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message, String uri) throws IOException
{
writer.write("CUSTOM\n");
super.writeErrorPageMessage(request,writer,code,message,uri);
}
};
server.setErrorHandler(errorHandler);
String httpResponse = connector.getResponses("" +
"GET /ctx/path HTTP/1.1\r\n" +
@ -184,7 +197,7 @@ public class AsyncListenerTest
consumer.accept(event);
}
});
throw new TestRuntimeException();
throw new QuietServletException(new TestRuntimeException());
}
}), "/path/*");
context.addServlet(new ServletHolder(new HttpServlet()
@ -297,10 +310,18 @@ public class AsyncListenerTest
});
// Add a custom error page.
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
ErrorHandler errorHandler = new ErrorHandler()
{
@Override
protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message, String uri) throws IOException
{
writer.write("CUSTOM\n");
super.writeErrorPageMessage(request,writer,code,message,uri);
}
};
errorHandler.setServer(server);
errorHandler.addErrorPage(HttpStatus.BAD_GATEWAY_502, "/error");
server.addManaged(errorHandler);
server.setErrorHandler(errorHandler);
String httpResponse = connector.getResponses("" +
"GET / HTTP/1.1\r\n" +

View File

@ -486,10 +486,8 @@ public class AsyncServletTest
"onStartAsync",
"start",
"onTimeout",
"ERROR /ctx/path/error",
"!initial",
"onComplete"));
assertContains("ERROR DISPATCH: /ctx/path/error",response);
"onComplete")); // Error Page Loop!
assertContains("HTTP ERROR 500",response);
}
@Test

View File

@ -60,6 +60,7 @@ public class MultiPartInputStreamParser
protected MultipartConfigElement _config;
protected String _contentType;
protected MultiMap<Part> _parts;
protected Exception _err;
protected File _tmpDir;
protected File _contextTmpDir;
protected boolean _deleteOnExit;
@ -421,6 +422,9 @@ public class MultiPartInputStreamParser
throws IOException
{
parse();
throwIfError();
Collection<List<Part>> values = _parts.values();
List<Part> parts = new ArrayList<>();
for (List<Part> o: values)
@ -443,20 +447,36 @@ public class MultiPartInputStreamParser
throws IOException
{
parse();
throwIfError();
return _parts.getValue(name, 0);
}
/**
* Throws an exception if one has been latched.
*
* @throws IOException
*/
protected void throwIfError ()
throws IOException
{
if (_err != null)
{
if (_err instanceof IOException)
throw (IOException)_err;
if (_err instanceof IllegalStateException)
throw (IllegalStateException)_err;
throw new IllegalStateException(_err);
}
}
/**
* Parse, if necessary, the multipart stream.
*
* @throws IOException if unable to parse
*/
protected void parse ()
throws IOException
{
//have we already parsed the input?
if (_parts != null)
if (_parts != null || _err != null)
return;
//initialize
@ -467,233 +487,253 @@ public class MultiPartInputStreamParser
if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
return;
//sort out the location to which to write the files
if (_config.getLocation() == null)
_tmpDir = _contextTmpDir;
else if ("".equals(_config.getLocation()))
_tmpDir = _contextTmpDir;
else
{
File f = new File (_config.getLocation());
if (f.isAbsolute())
_tmpDir = f;
else
_tmpDir = new File (_contextTmpDir, _config.getLocation());
}
if (!_tmpDir.exists())
_tmpDir.mkdirs();
String contentTypeBoundary = "";
int bstart = _contentType.indexOf("boundary=");
if (bstart >= 0)
{
int bend = _contentType.indexOf(";", bstart);
bend = (bend < 0? _contentType.length(): bend);
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim());
}
String boundary="--"+contentTypeBoundary;
String lastBoundary=boundary+"--";
byte[] byteBoundary=lastBoundary.getBytes(StandardCharsets.ISO_8859_1);
// Get first boundary
String line = null;
try
{
line=((ReadLineInputStream)_in).readLine();
}
catch (IOException e)
{
LOG.warn("Badly formatted multipart request");
throw e;
}
//sort out the location to which to write the files
if (line == null)
throw new IOException("Missing content for multipart request");
boolean badFormatLogged = false;
line=line.trim();
while (line != null && !line.equals(boundary) && !line.equals(lastBoundary))
{
if (!badFormatLogged)
{
LOG.warn("Badly formatted multipart request");
badFormatLogged = true;
}
line=((ReadLineInputStream)_in).readLine();
line=(line==null?line:line.trim());
}
if (line == null || line.length() == 0)
throw new IOException("Missing initial multi part boundary");
// Empty multipart.
if (line.equals(lastBoundary))
return;
// Read each part
boolean lastPart=false;
outer:while(!lastPart)
{
String contentDisposition=null;
String contentType=null;
String contentTransferEncoding=null;
MultiMap<String> headers = new MultiMap<>();
while(true)
{
line=((ReadLineInputStream)_in).readLine();
//No more input
if(line==null)
break outer;
//end of headers:
if("".equals(line))
break;
total += line.length();
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
//get content-disposition and content-type
int c=line.indexOf(':',0);
if(c>0)
{
String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
String value=line.substring(c+1,line.length()).trim();
headers.put(key, value);
if (key.equalsIgnoreCase("content-disposition"))
contentDisposition=value;
if (key.equalsIgnoreCase("content-type"))
contentType = value;
if(key.equals("content-transfer-encoding"))
contentTransferEncoding=value;
}
}
// Extract content-disposition
boolean form_data=false;
if(contentDisposition==null)
{
throw new IOException("Missing content-disposition");
}
QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
String name=null;
String filename=null;
while(tok.hasMoreTokens())
{
String t=tok.nextToken().trim();
String tl=t.toLowerCase(Locale.ENGLISH);
if(t.startsWith("form-data"))
form_data=true;
else if(tl.startsWith("name="))
name=value(t);
else if(tl.startsWith("filename="))
filename=filenameValue(t);
}
// Check disposition
if(!form_data)
{
continue;
}
//It is valid for reset and submit buttons to have an empty name.
//If no name is supplied, the browser skips sending the info for that field.
//However, if you supply the empty string as the name, the browser sends the
//field, with name as the empty string. So, only continue this loop if we
//have not yet seen a name field.
if(name==null)
{
continue;
}
//Have a new Part
MultiPart part = new MultiPart(name, filename);
part.setHeaders(headers);
part.setContentType(contentType);
_parts.add(name, part);
part.open();
InputStream partInput = null;
if ("base64".equalsIgnoreCase(contentTransferEncoding))
{
partInput = new Base64InputStream((ReadLineInputStream)_in);
}
else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
{
partInput = new FilterInputStream(_in)
{
@Override
public int read() throws IOException
{
int c = in.read();
if (c >= 0 && c == '=')
{
int hi = in.read();
int lo = in.read();
if (hi < 0 || lo < 0)
{
throw new IOException("Unexpected end to quoted-printable byte");
}
char[] chars = new char[] { (char)hi, (char)lo };
c = Integer.parseInt(new String(chars),16);
}
return c;
}
};
}
if (_config.getLocation() == null)
_tmpDir = _contextTmpDir;
else if ("".equals(_config.getLocation()))
_tmpDir = _contextTmpDir;
else
partInput = _in;
{
File f = new File (_config.getLocation());
if (f.isAbsolute())
_tmpDir = f;
else
_tmpDir = new File (_contextTmpDir, _config.getLocation());
}
if (!_tmpDir.exists())
_tmpDir.mkdirs();
String contentTypeBoundary = "";
int bstart = _contentType.indexOf("boundary=");
if (bstart >= 0)
{
int bend = _contentType.indexOf(";", bstart);
bend = (bend < 0? _contentType.length(): bend);
contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend)).trim());
}
String boundary="--"+contentTypeBoundary;
String lastBoundary=boundary+"--";
byte[] byteBoundary=lastBoundary.getBytes(StandardCharsets.ISO_8859_1);
// Get first boundary
String line = null;
try
{
int state=-2;
int c;
boolean cr=false;
boolean lf=false;
line=((ReadLineInputStream)_in).readLine();
}
catch (IOException e)
{
LOG.warn("Badly formatted multipart request");
throw e;
}
// loop for all lines
if (line == null)
throw new IOException("Missing content for multipart request");
boolean badFormatLogged = false;
line=line.trim();
while (line != null && !line.equals(boundary) && !line.equals(lastBoundary))
{
if (!badFormatLogged)
{
LOG.warn("Badly formatted multipart request");
badFormatLogged = true;
}
line=((ReadLineInputStream)_in).readLine();
line=(line==null?line:line.trim());
}
if (line == null || line.length() == 0)
throw new IOException("Missing initial multi part boundary");
// Empty multipart.
if (line.equals(lastBoundary))
return;
// Read each part
boolean lastPart=false;
outer:while(!lastPart)
{
String contentDisposition=null;
String contentType=null;
String contentTransferEncoding=null;
MultiMap<String> headers = new MultiMap<>();
while(true)
{
int b=0;
while((c=(state!=-2)?state:partInput.read())!=-1)
line=((ReadLineInputStream)_in).readLine();
//No more input
if(line==null)
break outer;
//end of headers:
if("".equals(line))
break;
total += line.length();
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
//get content-disposition and content-type
int c=line.indexOf(':',0);
if(c>0)
{
total ++;
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
String value=line.substring(c+1,line.length()).trim();
headers.put(key, value);
if (key.equalsIgnoreCase("content-disposition"))
contentDisposition=value;
if (key.equalsIgnoreCase("content-type"))
contentType = value;
if(key.equals("content-transfer-encoding"))
contentTransferEncoding=value;
}
}
state=-2;
// Extract content-disposition
boolean form_data=false;
if(contentDisposition==null)
{
throw new IOException("Missing content-disposition");
}
// look for CR and/or LF
if(c==13||c==10)
QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
String name=null;
String filename=null;
while(tok.hasMoreTokens())
{
String t=tok.nextToken().trim();
String tl=t.toLowerCase(Locale.ENGLISH);
if(t.startsWith("form-data"))
form_data=true;
else if(tl.startsWith("name="))
name=value(t);
else if(tl.startsWith("filename="))
filename=filenameValue(t);
}
// Check disposition
if(!form_data)
{
continue;
}
//It is valid for reset and submit buttons to have an empty name.
//If no name is supplied, the browser skips sending the info for that field.
//However, if you supply the empty string as the name, the browser sends the
//field, with name as the empty string. So, only continue this loop if we
//have not yet seen a name field.
if(name==null)
{
continue;
}
//Have a new Part
MultiPart part = new MultiPart(name, filename);
part.setHeaders(headers);
part.setContentType(contentType);
_parts.add(name, part);
part.open();
InputStream partInput = null;
if ("base64".equalsIgnoreCase(contentTransferEncoding))
{
partInput = new Base64InputStream((ReadLineInputStream)_in);
}
else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
{
partInput = new FilterInputStream(_in)
{
@Override
public int read() throws IOException
{
if(c==13)
int c = in.read();
if (c >= 0 && c == '=')
{
partInput.mark(1);
int tmp=partInput.read();
if (tmp!=10)
partInput.reset();
else
state=tmp;
int hi = in.read();
int lo = in.read();
if (hi < 0 || lo < 0)
{
throw new IOException("Unexpected end to quoted-printable byte");
}
char[] chars = new char[] { (char)hi, (char)lo };
c = Integer.parseInt(new String(chars),16);
}
return c;
}
};
}
else
partInput = _in;
try
{
int state=-2;
int c;
boolean cr=false;
boolean lf=false;
// loop for all lines
while(true)
{
int b=0;
while((c=(state!=-2)?state:partInput.read())!=-1)
{
total ++;
if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
state=-2;
// look for CR and/or LF
if(c==13||c==10)
{
if(c==13)
{
partInput.mark(1);
int tmp=partInput.read();
if (tmp!=10)
partInput.reset();
else
state=tmp;
}
break;
}
// Look for boundary
if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
{
b++;
}
else
{
// Got a character not part of the boundary, so we don't have the boundary marker.
// Write out as many chars as we matched, then the char we're looking at.
if(cr)
part.write(13);
if(lf)
part.write(10);
cr=lf=false;
if(b>0)
part.write(byteBoundary,0,b);
b=-1;
part.write(c);
}
break;
}
// Look for boundary
if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
// Check for incomplete boundary match, writing out the chars we matched along the way
if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
{
b++;
}
else
{
// Got a character not part of the boundary, so we don't have the boundary marker.
// Write out as many chars as we matched, then the char we're looking at.
if(cr)
part.write(13);
@ -701,60 +741,47 @@ public class MultiPartInputStreamParser
part.write(10);
cr=lf=false;
if(b>0)
part.write(byteBoundary,0,b);
part.write(byteBoundary,0,b);
b=-1;
part.write(c);
}
}
// Check for incomplete boundary match, writing out the chars we matched along the way
if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
{
// Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
if(b>0||c==-1)
{
if(b==byteBoundary.length)
lastPart=true;
if(state==10)
state=-2;
break;
}
// handle CR LF
if(cr)
part.write(13);
if(lf)
part.write(10);
cr=lf=false;
part.write(byteBoundary,0,b);
b=-1;
}
// Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
if(b>0||c==-1)
{
if(b==byteBoundary.length)
lastPart=true;
cr=(c==13);
lf=(c==10||state==10);
if(state==10)
state=-2;
break;
}
}
finally
{
// handle CR LF
if(cr)
part.write(13);
if(lf)
part.write(10);
cr=(c==13);
lf=(c==10||state==10);
if(state==10)
state=-2;
part.close();
}
}
finally
{
part.close();
}
if (!lastPart)
throw new IOException("Incomplete parts");
}
catch (Exception e)
{
_err = e;
}
if (!lastPart)
throw new IOException("Incomplete parts");
}
public void setDeleteOnExit(boolean deleteOnExit)

View File

@ -354,6 +354,42 @@ public class MultiPartInputStreamTest
assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
}
}
@Test
public void testRequestTooBigThrowsErrorOnGetParts ()
throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = null;
//cause parsing
try
{
parts = mpis.getParts();
fail("Request should have exceeded maxRequestSize");
}
catch (IllegalStateException e)
{
assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
}
//try again
try
{
parts = mpis.getParts();
fail("Request should have exceeded maxRequestSize");
}
catch (IllegalStateException e)
{
assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
}
}
@Test
public void testFileTooBig()
@ -376,6 +412,41 @@ public class MultiPartInputStreamTest
assertTrue(e.getMessage().startsWith("Multipart Mime part"));
}
}
@Test
public void testFileTooBigThrowsErrorOnGetParts()
throws Exception
{
MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30);
MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(new ByteArrayInputStream(_multi.getBytes()),
_contentType,
config,
_tmpDir);
mpis.setDeleteOnExit(true);
Collection<Part> parts = null;
try
{
parts = mpis.getParts(); //caused parsing
fail("stuff.txt should have been larger than maxFileSize");
}
catch (IllegalStateException e)
{
assertTrue(e.getMessage().startsWith("Multipart Mime part"));
}
//test again after the parsing
try
{
parts = mpis.getParts(); //caused parsing
fail("stuff.txt should have been larger than maxFileSize");
}
catch (IllegalStateException e)
{
assertTrue(e.getMessage().startsWith("Multipart Mime part"));
}
}
@Test
public void testPartFileNotDeleted () throws Exception

View File

@ -529,9 +529,9 @@ public abstract class RFC2616BaseTest
req4.append("\n"); // no virtual host
HttpTester.Response response = http.request(req4);
System.err.println(response);
assertEquals("5.2 No Host",HttpStatus.BAD_REQUEST_400,response.getStatus());
assertEquals("5.2 No Host","", response.getContent());
}
/**

View File

@ -55,6 +55,7 @@ public class InfinispanTestSessionServer extends AbstractTestServer
InfinispanSessionIdManager idManager = new InfinispanSessionIdManager(getServer());
idManager.setWorkerName("w"+(__workers++));
idManager.setCache((BasicCache)config);
idManager.setInfinispanIdleTimeoutSec(0);
return idManager;
}