Merge remote-tracking branch 'origin/master' into jetty-9.4.x-Feature
This commit is contained in:
commit
5263f15acd
|
@ -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)
|
||||
|
|
16
README.md
16
README.md
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -470,7 +470,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG.info(_request.getRequestURI(), failure);
|
||||
LOG.warn(_request.getRequestURI(), failure);
|
||||
}
|
||||
|
||||
try
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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" +
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue