Merge branch 'jetty-9.1' into release-9.1

This commit is contained in:
Jesse McConnell 2013-10-31 14:55:04 -05:00
commit 54726c67a7
45 changed files with 1041 additions and 488 deletions

View File

@ -692,7 +692,6 @@ public class AnnotationParser
parseDir(handlers, res, resolver);
else
{
System.err.println("TRYING TO SCAN "+res);
//we've already verified the directories, so just verify the class file name
File file = res.getFile();
if (isValidClassFileName((file==null?null:file.getName())))

View File

@ -40,12 +40,7 @@ public class AntWebInfConfiguration extends WebInfConfiguration
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
// Look for a work directory
File work = findWorkDirectory(context);
if (work != null)
makeTempDirectory(work, context, false);
{
//Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);

View File

@ -131,8 +131,7 @@ public class HttpConversation extends AttributesMap
// will notify a listener that may send a new request and trigger
// another call to this method which will build different listeners
// which may be iterated over when the iteration continues.
listeners = new ArrayList<>();
List<Response.ResponseListener> listeners = new ArrayList<>();
HttpExchange firstExchange = exchanges.peekFirst();
HttpExchange lastExchange = exchanges.peekLast();
if (firstExchange == lastExchange)
@ -151,6 +150,7 @@ public class HttpConversation extends AttributesMap
else
listeners.addAll(firstExchange.getResponseListeners());
}
this.listeners = listeners;
}
public void complete()

View File

@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Path;
import java.util.ArrayList;
@ -296,93 +297,184 @@ public class HttpRequest implements Request
}
@Override
public Request onRequestQueued(QueuedListener listener)
public Request onRequestQueued(final QueuedListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new QueuedListener()
{
@Override
public void onQueued(Request request)
{
listener.onQueued(request);
}
});
return this;
}
@Override
public Request onRequestBegin(BeginListener listener)
public Request onRequestBegin(final BeginListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new BeginListener()
{
@Override
public void onBegin(Request request)
{
listener.onBegin(request);
}
});
return this;
}
@Override
public Request onRequestHeaders(HeadersListener listener)
public Request onRequestHeaders(final HeadersListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new HeadersListener()
{
@Override
public void onHeaders(Request request)
{
listener.onHeaders(request);
}
});
return this;
}
@Override
public Request onRequestCommit(CommitListener listener)
public Request onRequestCommit(final CommitListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new CommitListener()
{
@Override
public void onCommit(Request request)
{
listener.onCommit(request);
}
});
return this;
}
@Override
public Request onRequestContent(ContentListener listener)
public Request onRequestContent(final ContentListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new ContentListener()
{
@Override
public void onContent(Request request, ByteBuffer content)
{
listener.onContent(request, content);
}
});
return this;
}
@Override
public Request onRequestSuccess(SuccessListener listener)
public Request onRequestSuccess(final SuccessListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new SuccessListener()
{
@Override
public void onSuccess(Request request)
{
listener.onSuccess(request);
}
});
return this;
}
@Override
public Request onRequestFailure(FailureListener listener)
public Request onRequestFailure(final FailureListener listener)
{
this.requestListeners.add(listener);
this.requestListeners.add(new FailureListener()
{
@Override
public void onFailure(Request request, Throwable failure)
{
listener.onFailure(request, failure);
}
});
return this;
}
@Override
public Request onResponseBegin(Response.BeginListener listener)
public Request onResponseBegin(final Response.BeginListener listener)
{
this.responseListeners.add(listener);
this.responseListeners.add(new Response.BeginListener()
{
@Override
public void onBegin(Response response)
{
listener.onBegin(response);
}
});
return this;
}
@Override
public Request onResponseHeader(Response.HeaderListener listener)
public Request onResponseHeader(final Response.HeaderListener listener)
{
this.responseListeners.add(listener);
this.responseListeners.add(new Response.HeaderListener()
{
@Override
public boolean onHeader(Response response, HttpField field)
{
return listener.onHeader(response, field);
}
});
return this;
}
@Override
public Request onResponseHeaders(Response.HeadersListener listener)
public Request onResponseHeaders(final Response.HeadersListener listener)
{
this.responseListeners.add(listener);
this.responseListeners.add(new Response.HeadersListener()
{
@Override
public void onHeaders(Response response)
{
listener.onHeaders(response);
}
});
return this;
}
@Override
public Request onResponseContent(Response.ContentListener listener)
public Request onResponseContent(final Response.ContentListener listener)
{
this.responseListeners.add(listener);
this.responseListeners.add(new Response.ContentListener()
{
@Override
public void onContent(Response response, ByteBuffer content)
{
listener.onContent(response, content);
}
});
return this;
}
@Override
public Request onResponseSuccess(Response.SuccessListener listener)
public Request onResponseSuccess(final Response.SuccessListener listener)
{
this.responseListeners.add(listener);
this.responseListeners.add(new Response.SuccessListener()
{
@Override
public void onSuccess(Response response)
{
listener.onSuccess(response);
}
});
return this;
}
@Override
public Request onResponseFailure(Response.FailureListener listener)
public Request onResponseFailure(final Response.FailureListener listener)
{
this.responseListeners.add(listener);
this.responseListeners.add(new Response.FailureListener()
{
@Override
public void onFailure(Response response, Throwable failure)
{
listener.onFailure(response, failure);
}
});
return this;
}

View File

@ -0,0 +1,189 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client;
import java.io.IOException;
import java.util.Arrays;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
public abstract class PoolingHttpDestination<C extends Connection> extends HttpDestination implements Promise<Connection>
{
private final ConnectionPool connectionPool;
public PoolingHttpDestination(HttpClient client, Origin origin)
{
super(client, origin);
this.connectionPool = newConnectionPool(client);
}
protected ConnectionPool newConnectionPool(HttpClient client)
{
return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
}
public ConnectionPool getConnectionPool()
{
return connectionPool;
}
@Override
@SuppressWarnings("unchecked")
public void succeeded(Connection connection)
{
process((C)connection, true);
}
@Override
public void failed(final Throwable x)
{
getHttpClient().getExecutor().execute(new Runnable()
{
@Override
public void run()
{
abort(x);
}
});
}
protected void send()
{
C connection = acquire();
if (connection != null)
process(connection, false);
}
@SuppressWarnings("unchecked")
public C acquire()
{
return (C)connectionPool.acquire();
}
/**
* <p>Processes a new connection making it idle or active depending on whether requests are waiting to be sent.</p>
* <p>A new connection is created when a request needs to be executed; it is possible that the request that
* triggered the request creation is executed by another connection that was just released, so the new connection
* may become idle.</p>
* <p>If a request is waiting to be executed, it will be dequeued and executed by the new connection.</p>
*
* @param connection the new connection
* @param dispatch whether to dispatch the processing to another thread
*/
public void process(final C connection, boolean dispatch)
{
HttpClient client = getHttpClient();
final HttpExchange exchange = getHttpExchanges().poll();
LOG.debug("Processing exchange {} on connection {}", exchange, connection);
if (exchange == null)
{
// TODO: review this part... may not be 100% correct
// TODO: e.g. is client is not running, there should be no need to close the connection
if (!connectionPool.release(connection))
connection.close();
if (!client.isRunning())
{
LOG.debug("{} is stopping", client);
connection.close();
}
}
else
{
final Request request = exchange.getRequest();
Throwable cause = request.getAbortCause();
if (cause != null)
{
abort(exchange, cause);
LOG.debug("Aborted before processing {}: {}", exchange, cause);
}
else
{
if (dispatch)
{
client.getExecutor().execute(new Runnable()
{
@Override
public void run()
{
send(connection, exchange);
}
});
}
else
{
send(connection, exchange);
}
}
}
}
protected abstract void send(C connection, HttpExchange exchange);
public void release(C connection)
{
LOG.debug("{} released", connection);
HttpClient client = getHttpClient();
if (client.isRunning())
{
if (connectionPool.isActive(connection))
process(connection, false);
else
LOG.debug("{} explicit", connection);
}
else
{
LOG.debug("{} is stopped", client);
close(connection);
connection.close();
}
}
@Override
public void close(Connection oldConnection)
{
super.close(oldConnection);
connectionPool.remove(oldConnection);
// We need to execute queued requests even if this connection failed.
// We may create a connection that is not needed, but it will eventually
// idle timeout, so no worries
if (!getHttpExchanges().isEmpty())
{
C newConnection = acquire();
if (newConnection != null)
process(newConnection, false);
}
}
public void close()
{
connectionPool.close();
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
ContainerLifeCycle.dump(out, indent, Arrays.asList(connectionPool));
}
}

View File

@ -18,173 +18,21 @@
package org.eclipse.jetty.client.http;
import java.io.IOException;
import java.util.Arrays;
import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.client.PoolingHttpDestination;
public class HttpDestinationOverHTTP extends HttpDestination implements Promise<Connection>
public class HttpDestinationOverHTTP extends PoolingHttpDestination<HttpConnectionOverHTTP>
{
private final ConnectionPool connectionPool;
public HttpDestinationOverHTTP(HttpClient client, Origin origin)
{
super(client, origin);
this.connectionPool = newConnectionPool(client);
}
protected ConnectionPool newConnectionPool(HttpClient client)
{
return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
}
public ConnectionPool getConnectionPool()
{
return connectionPool;
}
@Override
public void succeeded(Connection connection)
protected void send(HttpConnectionOverHTTP connection, HttpExchange exchange)
{
process((HttpConnectionOverHTTP)connection, true);
}
@Override
public void failed(final Throwable x)
{
getHttpClient().getExecutor().execute(new Runnable()
{
@Override
public void run()
{
abort(x);
}
});
}
protected void send()
{
HttpConnectionOverHTTP connection = acquire();
if (connection != null)
process(connection, false);
}
protected HttpConnectionOverHTTP acquire()
{
return (HttpConnectionOverHTTP)connectionPool.acquire();
}
/**
* <p>Processes a new connection making it idle or active depending on whether requests are waiting to be sent.</p>
* <p>A new connection is created when a request needs to be executed; it is possible that the request that
* triggered the request creation is executed by another connection that was just released, so the new connection
* may become idle.</p>
* <p>If a request is waiting to be executed, it will be dequeued and executed by the new connection.</p>
*
* @param connection the new connection
* @param dispatch whether to dispatch the processing to another thread
*/
protected void process(final HttpConnectionOverHTTP connection, boolean dispatch)
{
HttpClient client = getHttpClient();
final HttpExchange exchange = getHttpExchanges().poll();
LOG.debug("Processing exchange {} on connection {}", exchange, connection);
if (exchange == null)
{
// TODO: review this part... may not be 100% correct
// TODO: e.g. is client is not running, there should be no need to close the connection
if (!connectionPool.release(connection))
connection.close();
if (!client.isRunning())
{
LOG.debug("{} is stopping", client);
connection.close();
}
}
else
{
final Request request = exchange.getRequest();
Throwable cause = request.getAbortCause();
if (cause != null)
{
abort(exchange, cause);
LOG.debug("Aborted before processing {}: {}", exchange, cause);
}
else
{
if (dispatch)
{
client.getExecutor().execute(new Runnable()
{
@Override
public void run()
{
connection.send(exchange);
}
});
}
else
{
connection.send(exchange);
}
}
}
}
protected void release(HttpConnectionOverHTTP connection)
{
LOG.debug("{} released", connection);
HttpClient client = getHttpClient();
if (client.isRunning())
{
if (connectionPool.isActive(connection))
process(connection, false);
else
LOG.debug("{} explicit", connection);
}
else
{
LOG.debug("{} is stopped", client);
close(connection);
connection.close();
}
}
@Override
public void close(Connection oldConnection)
{
super.close(oldConnection);
connectionPool.remove(oldConnection);
// We need to execute queued requests even if this connection failed.
// We may create a connection that is not needed, but it will eventually
// idle timeout, so no worries
if (!getHttpExchanges().isEmpty())
{
HttpConnectionOverHTTP newConnection = acquire();
if (newConnection != null)
process(newConnection, false);
}
}
public void close()
{
connectionPool.close();
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
ContainerLifeCycle.dump(out, indent, Arrays.asList(connectionPool));
connection.send(exchange);
}
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.client.util;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Iterator;
@ -35,14 +36,18 @@ import org.eclipse.jetty.util.log.Logger;
* Invocations to the {@link #iterator()} method after the first will return an "empty" iterator
* because the stream has been consumed on the first invocation.
* <p />
* It is possible to specify, at the constructor, a buffer size used to read content from the
* stream, by default 4096 bytes.
* <p />
* However, it is possible for subclasses to override {@link #onRead(byte[], int, int)} to copy
* the content read from the stream to another location (for example a file), and be able to
* support multiple invocations of {@link #iterator()}, returning the iterator provided by this
* class on the first invocation, and an iterator on the bytes copied to the other location
* for subsequent invocations.
* <p />
* It is possible to specify, at the constructor, a buffer size used to read content from the
* stream, by default 4096 bytes.
* <p />
* The {@link InputStream} passed to the constructor is by default closed when is it fully
* consumed (or when an exception is thrown while reading it), unless otherwise specified
* to the {@link #InputStreamContentProvider(java.io.InputStream, int, boolean) constructor}.
*/
public class InputStreamContentProvider implements ContentProvider
{
@ -50,6 +55,7 @@ public class InputStreamContentProvider implements ContentProvider
private final InputStream stream;
private final int bufferSize;
private final boolean autoClose;
public InputStreamContentProvider(InputStream stream)
{
@ -57,9 +63,15 @@ public class InputStreamContentProvider implements ContentProvider
}
public InputStreamContentProvider(InputStream stream, int bufferSize)
{
this(stream, bufferSize, true);
}
public InputStreamContentProvider(InputStream stream, int bufferSize, boolean autoClose)
{
this.stream = stream;
this.bufferSize = bufferSize;
this.autoClose = autoClose;
}
@Override
@ -136,6 +148,7 @@ public class InputStreamContentProvider implements ContentProvider
else if (read < 0)
{
hasNext = Boolean.FALSE;
close();
return false;
}
else
@ -154,6 +167,7 @@ public class InputStreamContentProvider implements ContentProvider
// Signal we have more content to cause a call to
// next() which will throw NoSuchElementException.
hasNext = Boolean.TRUE;
close();
return true;
}
throw new IllegalStateException();
@ -178,5 +192,20 @@ public class InputStreamContentProvider implements ContentProvider
{
throw new UnsupportedOperationException();
}
private void close()
{
if (autoClose)
{
try
{
stream.close();
}
catch (IOException x)
{
LOG.ignore(x);
}
}
}
}
}

View File

@ -863,4 +863,148 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testRequestListenerForMultipleEventsIsInvokedOncePerEvent() throws Exception
{
start(new EmptyServerHandler());
final AtomicInteger counter = new AtomicInteger();
Request.Listener listener = new Request.Listener()
{
@Override
public void onQueued(Request request)
{
counter.incrementAndGet();
}
@Override
public void onBegin(Request request)
{
counter.incrementAndGet();
}
@Override
public void onHeaders(Request request)
{
counter.incrementAndGet();
}
@Override
public void onCommit(Request request)
{
counter.incrementAndGet();
}
@Override
public void onContent(Request request, ByteBuffer content)
{
// Should not be invoked
counter.incrementAndGet();
}
@Override
public void onFailure(Request request, Throwable failure)
{
// Should not be invoked
counter.incrementAndGet();
}
@Override
public void onSuccess(Request request)
{
counter.incrementAndGet();
}
};
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.onRequestQueued(listener)
.onRequestBegin(listener)
.onRequestHeaders(listener)
.onRequestCommit(listener)
.onRequestContent(listener)
.onRequestSuccess(listener)
.onRequestFailure(listener)
.listener(listener)
.send();
Assert.assertEquals(200, response.getStatus());
int expectedEventsTriggeredByOnRequestXXXListeners = 5;
int expectedEventsTriggeredByListener = 5;
int expected = expectedEventsTriggeredByOnRequestXXXListeners + expectedEventsTriggeredByListener;
Assert.assertEquals(expected, counter.get());
}
@Test
public void testResponseListenerForMultipleEventsIsInvokedOncePerEvent() throws Exception
{
start(new EmptyServerHandler());
final AtomicInteger counter = new AtomicInteger();
final CountDownLatch latch = new CountDownLatch(1);
Response.Listener listener = new Response.Listener()
{
@Override
public void onBegin(Response response)
{
counter.incrementAndGet();
}
@Override
public boolean onHeader(Response response, HttpField field)
{
// Number of header may vary, so don't count
return true;
}
@Override
public void onHeaders(Response response)
{
counter.incrementAndGet();
}
@Override
public void onContent(Response response, ByteBuffer content)
{
// Should not be invoked
counter.incrementAndGet();
}
@Override
public void onSuccess(Response response)
{
counter.incrementAndGet();
}
@Override
public void onFailure(Response response, Throwable failure)
{
// Should not be invoked
counter.incrementAndGet();
}
@Override
public void onComplete(Result result)
{
Assert.assertEquals(200, result.getResponse().getStatus());
counter.incrementAndGet();
latch.countDown();
}
};
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.onResponseBegin(listener)
.onResponseHeader(listener)
.onResponseHeaders(listener)
.onResponseContent(listener)
.onResponseSuccess(listener)
.onResponseFailure(listener)
.send(listener);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
int expectedEventsTriggeredByOnResponseXXXListeners = 3;
int expectedEventsTriggeredByCompletionListener = 4;
int expected = expectedEventsTriggeredByOnResponseXXXListeners + expectedEventsTriggeredByCompletionListener;
Assert.assertEquals(expected, counter.get());
}
}

View File

@ -91,7 +91,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))
{
@Override
protected void process(HttpConnectionOverHTTP connection, boolean dispatch)
public void process(HttpConnectionOverHTTP connection, boolean dispatch)
{
try
{

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.io;
import java.io.Closeable;
import org.eclipse.jetty.util.Callback;
/**
@ -28,7 +30,7 @@ import org.eclipse.jetty.util.Callback;
* and when the {@link EndPoint} signals read readyness, this {@link Connection} can
* read bytes from the network and interpret them.</p>
*/
public interface Connection extends AutoCloseable
public interface Connection extends Closeable
{
public void addListener(Listener listener);

View File

@ -25,8 +25,6 @@ import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
@ -58,14 +56,10 @@ import org.eclipse.jetty.util.thread.Scheduler;
*/
public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
{
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
public static final String SUBMIT_KEY_UPDATES="org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
/**
* The default connect timeout, in milliseconds
*/
public static final String SUBMIT_KEY_UPDATES = "org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
private final static boolean __submitKeyUpdates=Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES,"FALSE"));
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
private final static boolean __submitKeyUpdates = Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES, "false"));
private final Executor executor;
private final Scheduler scheduler;
@ -233,7 +227,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
connection.onOpen();
}
catch (Exception x)
catch (Throwable x)
{
if (isRunning())
LOG.warn("Exception while notifying connection " + connection, x);
@ -253,9 +247,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
connection.onClose();
}
catch (Exception x)
catch (Throwable x)
{
LOG.info("Exception while notifying connection " + connection, x);
LOG.debug("Exception while notifying connection " + connection, x);
}
}
@ -368,11 +362,12 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
public void updateKey(Runnable update)
{
if (__submitKeyUpdates)
{
submit(update);
}
else
{
update.run();
runChange(update);
if (_state.compareAndSet(State.SELECT, State.WAKEUP))
wakeup();
}
@ -435,8 +430,15 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
protected void runChange(Runnable change)
{
LOG.debug("Running change {}", change);
change.run();
try
{
LOG.debug("Running change {}", change);
change.run();
}
catch (Throwable x)
{
LOG.debug("Could not run change " + change, x);
}
}
@Override
@ -520,7 +522,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
}
selectedKeys.clear();
}
catch (Exception x)
catch (Throwable x)
{
if (isRunning())
LOG.warn(x);
@ -553,7 +555,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
if (attachment instanceof EndPoint)
((EndPoint)attachment).close();
}
catch (Exception x)
catch (Throwable x)
{
LOG.warn("Could not process key for channel " + key.channel(), x);
if (attachment instanceof EndPoint)
@ -563,10 +565,10 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
private void processConnect(SelectionKey key, Connect connect)
{
key.attach(connect.attachment);
SocketChannel channel = (SocketChannel)key.channel();
try
{
key.attach(connect.attachment);
boolean connected = finishConnect(channel);
if (connected)
{
@ -580,10 +582,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
throw new ConnectException();
}
}
catch (Exception x)
catch (Throwable x)
{
connect.failed(x);
closeNoExceptions(channel);
}
}
@ -593,7 +594,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
closeable.close();
}
catch (IOException x)
catch (Throwable x)
{
LOG.ignore(x);
}
@ -740,8 +741,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
EndPoint endpoint = createEndPoint(_channel, key);
key.attach(endpoint);
}
catch (IOException x)
catch (Throwable x)
{
closeNoExceptions(_channel);
LOG.debug(x);
}
}
@ -768,7 +770,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
channel.register(_selector, SelectionKey.OP_CONNECT, this);
}
catch (ClosedSelectorException | ClosedChannelException x)
catch (Throwable x)
{
failed(x);
}
@ -779,22 +781,10 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
if (failed.compareAndSet(false, true))
{
timeout.cancel();
close();
closeNoExceptions(channel);
connectionFailed(channel, failure, attachment);
}
}
private void close()
{
try
{
channel.close();
}
catch (IOException x)
{
LOG.ignore(x);
}
}
}
private class ConnectTimeout implements Runnable
@ -878,7 +868,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
{
try
{
endPoint.getConnection().close();
closeNoExceptions(endPoint.getConnection());
}
finally
{

View File

@ -31,6 +31,7 @@ import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
@ -199,9 +200,14 @@ public abstract class AbstractLoginModule implements LoginModule
callbacks[2] = new PasswordCallback("Enter password", false); //only used if framework does not support the ObjectCallback
return callbacks;
}
public boolean isIgnored ()
{
return false;
}
public abstract UserInfo getUserInfo (String username) throws Exception;
@ -214,7 +220,10 @@ public abstract class AbstractLoginModule implements LoginModule
public boolean login() throws LoginException
{
try
{
{
if (isIgnored())
return false;
if (callbackHandler == null)
throw new LoginException ("No callback handler");
@ -231,7 +240,7 @@ public abstract class AbstractLoginModule implements LoginModule
if ((webUserName == null) || (webCredential == null))
{
setAuthenticated(false);
return isAuthenticated();
throw new FailedLoginException();
}
UserInfo userInfo = getUserInfo(webUserName);
@ -239,12 +248,16 @@ public abstract class AbstractLoginModule implements LoginModule
if (userInfo == null)
{
setAuthenticated(false);
return isAuthenticated();
throw new FailedLoginException();
}
currentUser = new JAASUserInfo(userInfo);
setAuthenticated(currentUser.checkCredential(webCredential));
return isAuthenticated();
if (isAuthenticated())
return true;
else
throw new FailedLoginException();
}
catch (IOException e)
{
@ -256,7 +269,8 @@ public abstract class AbstractLoginModule implements LoginModule
}
catch (Exception e)
{
e.printStackTrace();
if (e instanceof LoginException)
throw (LoginException)e;
throw new LoginException (e.toString());
}
}

View File

@ -112,9 +112,10 @@ public class PropertyFileLoginModule extends AbstractLoginModule
PropertyUserStore propertyUserStore = _propertyUserStores.get(_filename);
if (propertyUserStore == null)
throw new IllegalStateException("PropertyUserStore should never be null here!");
LOG.debug("Checking PropertyUserStore "+_filename+" for "+userName);
UserIdentity userIdentity = propertyUserStore.getUserIdentity(userName);
if(userIdentity==null)
if (userIdentity==null)
return null;
Set<Principal> principals = userIdentity.getSubject().getPrincipals();
@ -127,7 +128,7 @@ public class PropertyFileLoginModule extends AbstractLoginModule
}
Credential credential = (Credential)userIdentity.getSubject().getPrivateCredentials().iterator().next();
LOG.debug("Found: " + userName + " in PropertyUserStore");
LOG.debug("Found: " + userName + " in PropertyUserStore "+_filename);
return new UserInfo(userName, credential, roles);
}

View File

@ -288,6 +288,9 @@ public class JspcMojo extends AbstractMojo
Thread.currentThread().setContextClassLoader(webAppClassLoader);
if (jspc == null)
jspc = new JspC();
jspc.setWebXmlFragment(webXmlFragment);
jspc.setUriroot(webAppSourceDirectory);
jspc.setOutputDir(generatedClasses);

View File

@ -132,11 +132,20 @@ public class JettyRunForkedMojo extends AbstractMojo
* The temporary directory to use for the webapp.
* Defaults to target/tmp
*
* @parameter expression="${project.build.directory}/tmp"
* @parameter alias="tmpDirectory" expression="${project.build.directory}/tmp"
* @required
* @readonly
*/
protected File tmpDirectory;
protected File tempDirectory;
/**
* Whether temporary directory contents should survive webapp restarts.
*
* @parameter default-value="false"
*/
private boolean persistTempDirectory;
/**
@ -418,12 +427,14 @@ public class JettyRunForkedMojo extends AbstractMojo
props.put("context.path", contextPath);
//sort out the tmp directory (make it if it doesn't exist)
if (tmpDirectory != null)
if (tempDirectory != null)
{
if (!tmpDirectory.exists())
tmpDirectory.mkdirs();
props.put("tmp.dir", tmpDirectory.getAbsolutePath());
if (!tempDirectory.exists())
tempDirectory.mkdirs();
props.put("tmp.dir", tempDirectory.getAbsolutePath());
}
props.put("tmp.dir.persist", Boolean.toString(persistTempDirectory));
//sort out base dir of webapp
if (webAppSourceDirectory == null || !webAppSourceDirectory.exists())

View File

@ -284,9 +284,6 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
{
LOG.debug("Unpacking overlay: " + overlay);
//resolve if not already resolved
resolveTempDirectory(context);
if (overlay.getResource() == null)
return null; //nothing to unpack

View File

@ -207,6 +207,9 @@ public class Starter
if (str != null)
webApp.setTempDirectory(new File(str.trim()));
str = (String)props.getProperty("tmp.dir.persist");
if (str != null)
webApp.setPersistTempDirectory(Boolean.valueOf(str));
// - the base directory
str = (String)props.getProperty("base.dir");
@ -219,7 +222,7 @@ public class Starter
// - put virtual webapp base resource first on resource path or not
str = (String)props.getProperty("base.first");
if (str != null && !"".equals(str.trim()))
webApp.setBaseAppFirst(Boolean.getBoolean(str));
webApp.setBaseAppFirst(Boolean.valueOf(str));
//For overlays

View File

@ -9,7 +9,7 @@
<name>Fragment1</name>
<ordering>
<after>others</after>
<after><others/></after>
</ordering>
<resource-ref>

View File

@ -9,13 +9,13 @@
<name>Fragment2</name>
<ordering>
<after>others</after>
<after><others/></after>
</ordering>
<resource-ref>
<res-ref-name>jdbc/mydatasource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>User</res-auth>
<res-auth>Application</res-auth>
<!--
<injection-target>
<injection-target-class>com.acme.Bar</injection-target-class>

View File

@ -9,7 +9,7 @@
<name>Fragment3</name>
<ordering>
<after>others</after>
<after><others/></after>
</ordering>
<resource-ref>

View File

@ -9,8 +9,8 @@
<Arg name="threadpool">
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Set name="minThreads">16</Set>
<Set name="maxThreads">256</Set>
<Set name="minThreads"><Property name="java.proxy.threadpool.min" default="16"/></Set>
<Set name="maxThreads"><Property name="java.proxy.threadpool.max" default="256"/></Set>
</New>
</Arg>
@ -20,7 +20,7 @@
<Arg name="server"><Ref refid="Proxy" /></Arg>
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.port" default="8888"/></Set>
<Set name="idleTimeout">300000</Set>
<Set name="idleTimeout"><Property name="java.proxy.idleTimeout" default="300000"/></Set>
</New>
</Arg>
</Call>
@ -34,7 +34,7 @@
<Arg>/</Arg>
<Call name="setInitParameter">
<Arg>maxThreads</Arg>
<Arg>128</Arg>
<Arg><Property name="java.proxy.threads.max" default="128"/></Arg>
</Call>
</Call>
</New>
@ -42,7 +42,7 @@
</New>
</Set>
<Set name="stopAtShutdown">true</Set>
<Set name="stopTimeout">1000</Set>
<Set name="stopAtShutdown"><Property name="java.proxy.stopAtShutdown" default="true"/></Set>
<Set name="stopTimeout"><Property name="java.proxy.stopTimeout" default="1000"/></Set>
</Configure>

View File

@ -11,3 +11,12 @@ lib/jetty-proxy-${jetty.version}.jar
[xml]
etc/jetty-proxy.xml
[ini-template]
## Proxy Configuration
jetty.proxy.threadpool.min=16
jetty.proxy.threadpool.max=256
jetty.proxy.idleTimeout=300000
jetty.proxy.threads.max=128
jetty.proxy.stopAtShutdown=true
jetty.proxy.stopTimeout=1000

View File

@ -137,6 +137,11 @@ public class ProxyServlet extends HttpServlet
}
}
public String getViaHost()
{
return _viaHost;
}
public long getTimeout()
{
return _timeout;
@ -419,11 +424,8 @@ public class ProxyServlet extends HttpServlet
proxyRequest.header(HttpHeader.HOST, _hostHeader);
// Add proxy headers
proxyRequest.header(HttpHeader.VIA, "http/1.1 " + _viaHost);
proxyRequest.header(HttpHeader.X_FORWARDED_FOR, request.getRemoteAddr());
proxyRequest.header(HttpHeader.X_FORWARDED_PROTO, request.getScheme());
proxyRequest.header(HttpHeader.X_FORWARDED_HOST, request.getHeader(HttpHeader.HOST.asString()));
proxyRequest.header(HttpHeader.X_FORWARDED_SERVER, request.getLocalName());
addViaHeader(proxyRequest);
addXForwardedHeaders(proxyRequest, request);
if (hasContent)
{
@ -488,6 +490,19 @@ public class ProxyServlet extends HttpServlet
proxyRequest.send(new ProxyResponseListener(request, response));
}
protected Request addViaHeader(Request proxyRequest)
{
return proxyRequest.header(HttpHeader.VIA, "http/1.1 " + getViaHost());
}
protected void addXForwardedHeaders(Request proxyRequest, HttpServletRequest request)
{
proxyRequest.header(HttpHeader.X_FORWARDED_FOR, request.getRemoteAddr());
proxyRequest.header(HttpHeader.X_FORWARDED_PROTO, request.getScheme());
proxyRequest.header(HttpHeader.X_FORWARDED_HOST, request.getHeader(HttpHeader.HOST.asString()));
proxyRequest.header(HttpHeader.X_FORWARDED_SERVER, request.getLocalName());
}
protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
{
for (HttpField field : proxyResponse.getHeaders())

View File

@ -44,9 +44,9 @@ service jetty
-->
<!-- sane defaults -->
<Set name="idleTimeout">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="idleTimeout"><Property name="jetty.xinetd.idleTimeout" default="300000"/></Set>
<Set name="Acceptors"><Property name="jetty.xinetd.acceptors" default="2"/></Set>
<Set name="statsOn"><Property name="jetty.xinetd.statsOn" default="false"/></Set>
</New>
</Arg>
</Call>

View File

@ -1,2 +1,11 @@
#
# Module to add all lib/ext/*.jar files to classpath
#
[lib]
lib/ext/*.jar
[files]
lib/
lib/ext/

View File

@ -3,4 +3,8 @@
#
[lib]
resources
resources
[files]
resources/

View File

@ -7,3 +7,11 @@ server
[xml]
etc/jetty-xinetd.xml
[ini-template]
## Xinetd Configuration
## See ${jetty.home}/etc/jetty-xinetd.xml for example service entry
jetty.xinetd.idleTimeout=300000
jetty.xinetd.acceptors=2
jetty.xinetd.statsOn=false

View File

@ -420,6 +420,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
boolean committed = sendResponse(info, null, true);
if (!committed)
LOG.warn("Could not send response error 500: "+x);
_request.getAsyncContext().complete();
}
else if (isCommitted())
{

View File

@ -485,7 +485,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
{
case NEED_HEADER:
{
if (_lastContent && _content!=null && BufferUtil.space(_content)>_config.getResponseHeaderSize() && _content.hasArray() )
// Look for optimisation to avoid allocating a _header buffer
if (_lastContent && _content!=null && !_content.isReadOnly() && _content.hasArray() && BufferUtil.space(_content)>_config.getResponseHeaderSize() )
{
// use spare space in content buffer for header buffer
int p=_content.position();

View File

@ -302,7 +302,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
// write any remaining content in the buffer directly
if (len>0)
_channel.write(ByteBuffer.wrap(b, off, len), complete);
// pass as readonly to avoid space stealing optimisation in HttpConnection
_channel.write(ByteBuffer.wrap(b, off, len).asReadOnlyBuffer(), complete);
else if (complete)
_channel.write(BufferUtil.EMPTY_BUFFER,complete);
@ -440,12 +441,14 @@ public class HttpOutput extends ServletOutputStream implements Runnable
/* ------------------------------------------------------------ */
/** Blocking send of content.
* @param content The content to send
* @param content The content to send.
* @throws IOException
*/
public void sendContent(ByteBuffer content) throws IOException
{
final BlockingCallback callback =_channel.getWriteBlockingCallback();
if (content.hasArray()&&content.limit()<content.capacity())
content=content.asReadOnlyBuffer();
_channel.write(content,true,callback);
callback.block();
}
@ -487,7 +490,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
callback.block();
}
/* ------------------------------------------------------------ */
/** Asynchronous send of content.
* @param content The content to send
@ -495,6 +497,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
*/
public void sendContent(ByteBuffer content, final Callback callback)
{
if (content.hasArray()&&content.limit()<content.capacity())
content=content.asReadOnlyBuffer();
_channel.write(content,true,new Callback()
{
@Override

View File

@ -18,12 +18,13 @@
package org.eclipse.jetty.server;
import java.io.Closeable;
import java.io.IOException;
/**
* <p>A {@link Connector} for TCP/IP network connectors</p>
*/
public interface NetworkConnector extends Connector, AutoCloseable
public interface NetworkConnector extends Connector, Closeable
{
/**
* <p>Performs the activities needed to open the network communication

View File

@ -355,9 +355,10 @@ public class Server extends HandlerWrapper implements Attributes
futures.add(connector.shutdown());
// Then tell the contexts that we are shutting down
Handler[] contexts = getChildHandlersByClass(Graceful.class);
for (Handler context : contexts)
futures.add(((Graceful)context).shutdown());
Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
for (Handler graceful : gracefuls)
futures.add(((Graceful)graceful).shutdown());
// Shall we gracefully wait for zero connections?
long stopTimeout = getStopTimeout();

View File

@ -19,8 +19,11 @@
package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
@ -32,14 +35,16 @@ import org.eclipse.jetty.server.AsyncContextEvent;
import org.eclipse.jetty.server.HttpChannelState;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic;
@ManagedObject("Request Statistics Gathering")
public class StatisticsHandler extends HandlerWrapper
public class StatisticsHandler extends HandlerWrapper implements Graceful
{
private final AtomicLong _statsStartedAt = new AtomicLong();
@ -59,6 +64,8 @@ public class StatisticsHandler extends HandlerWrapper
private final AtomicInteger _responses5xx = new AtomicInteger();
private final AtomicLong _responsesTotalBytes = new AtomicLong();
private final AtomicReference<FutureCallback> _shutdown=new AtomicReference<>();
private final AsyncListener _onCompletion = new AsyncListener()
{
@Override
@ -86,14 +93,21 @@ public class StatisticsHandler extends HandlerWrapper
Request request = state.getBaseRequest();
final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
_requestStats.decrement();
long d=_requestStats.decrement();
_requestTimeStats.set(elapsed);
updateResponse(request);
_asyncWaitStats.decrement();
// If we have no more dispatches, should we signal shutdown?
if (d==0)
{
FutureCallback shutdown = _shutdown.get();
if (shutdown!=null)
shutdown.succeeded();
}
}
};
/**
@ -162,9 +176,18 @@ public class StatisticsHandler extends HandlerWrapper
}
else if (state.isInitial())
{
_requestStats.decrement();
long d=_requestStats.decrement();
_requestTimeStats.set(dispatched);
updateResponse(request);
// If we have no more dispatches, should we signal shutdown?
FutureCallback shutdown = _shutdown.get();
if (shutdown!=null)
{
httpResponse.flushBuffer();
if (d==0)
shutdown.succeeded();
}
}
// else onCompletion will handle it.
}
@ -205,9 +228,20 @@ public class StatisticsHandler extends HandlerWrapper
@Override
protected void doStart() throws Exception
{
_shutdown.set(null);
super.doStart();
statsReset();
}
@Override
protected void doStop() throws Exception
{
super.doStop();
FutureCallback shutdown = _shutdown.get();
if (shutdown!=null && !shutdown.isDone())
shutdown.failed(new TimeoutException());
}
/**
* @return the number of requests handled by this handler
@ -523,4 +557,15 @@ public class StatisticsHandler extends HandlerWrapper
return sb.toString();
}
@Override
public Future<Void> shutdown()
{
FutureCallback shutdown=new FutureCallback(false);
_shutdown.compareAndSet(null,shutdown);
shutdown=_shutdown.get();
if (_dispatchedStats.getCurrent()==0)
shutdown.succeeded();
return shutdown;
}
}

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class GracefulStopTest
{
private Server server;
@Before
public void setup() throws Exception
{
server = new Server(0);
StatisticsHandler stats = new StatisticsHandler();
TestHandler test=new TestHandler();
server.setHandler(stats);
stats.setHandler(test);
server.setStopTimeout(10 * 1000);
server.start();
}
@Test
public void testGraceful() throws Exception
{
new Thread()
{
@Override
public void run()
{
try
{
TimeUnit.SECONDS.sleep(1);
server.stop();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
try(Socket socket = new Socket("localhost",server.getBean(NetworkConnector.class).getLocalPort());)
{
socket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StringUtil.__ISO_8859_1_CHARSET));
String out = IO.toString(socket.getInputStream());
Assert.assertThat(out,Matchers.containsString("200 OK"));
}
}
private static class TestHandler extends AbstractHandler
{
@Override
public void handle(final String s, final Request request, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
throws IOException, ServletException
{
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
}
httpServletResponse.getWriter().write("OK");
httpServletResponse.setStatus(200);
request.setHandled(true);
}
}
}

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.FilterInputStream;
@ -26,6 +27,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
@ -81,6 +83,26 @@ public class HttpOutputTest
assertThat(response,containsString("HTTP/1.1 200 OK"));
}
@Test
public void testSendArray() throws Exception
{
byte[] buffer=new byte[16*1024];
Arrays.fill(buffer,0,4*1024,(byte)0x99);
Arrays.fill(buffer,4*1024,12*1024,(byte)0x58);
Arrays.fill(buffer,12*1024,16*1024,(byte)0x66);
_handler._content=ByteBuffer.wrap(buffer);
_handler._content.limit(12*1024);
_handler._content.position(4*1024);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("\r\nXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
for (int i=0;i<4*1024;i++)
assertEquals("i="+i,(byte)0x99,buffer[i]);
for (int i=12*1024;i<16*1024;i++)
assertEquals("i="+i,(byte)0x66,buffer[i]);
}
@Test
public void testSendInputStreamSimple() throws Exception
{
@ -386,9 +408,9 @@ public class HttpOutputTest
boolean _async;
ByteBuffer _buffer;
byte[] _bytes;
ByteBuffer _content;
InputStream _contentInputStream;
ReadableByteChannel _contentChannel;
ByteBuffer _content;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
@ -398,6 +420,7 @@ public class HttpOutputTest
final HttpOutput out = (HttpOutput) response.getOutputStream();
if (_contentInputStream!=null)
{
out.sendContent(_contentInputStream);
@ -510,10 +533,13 @@ public class HttpOutputTest
return;
}
if (_content!=null)
{
out.sendContent(_content);
response.setContentLength(_content.remaining());
if (_content.hasArray())
out.write(_content.array(),_content.arrayOffset()+_content.position(),_content.remaining());
else
out.sendContent(_content);
_content=null;
return;
}

View File

@ -82,9 +82,7 @@ public class SlowClientWithPipelinedRequestTest
}
}
// TODO merged from jetty-8 - not working???
@Test
@Ignore
public void testSlowClientWithPipelinedRequest() throws Exception
{
final int contentLength = 512 * 1024;

View File

@ -569,10 +569,10 @@ public class ServletHandler extends ScopedHandler
LOG.debug(request.toString());
}
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
if (!response.isCommitted())
{
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
if (th instanceof UnavailableException)
{
UnavailableException ue = (UnavailableException)th;
@ -586,6 +586,10 @@ public class ServletHandler extends ScopedHandler
}
else
LOG.debug("Response already committed",th);
// Complete async requests
if (request.isAsyncStarted())
request.getAsyncContext().complete();
}
catch(Error e)
{
@ -596,15 +600,16 @@ public class ServletHandler extends ScopedHandler
LOG.warn("Error for "+request.getRequestURI(),e);
if(LOG.isDebugEnabled())LOG.debug(request.toString());
// TODO httpResponse.getHttpConnection().forceClose();
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
if (!response.isCommitted())
{
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
else
LOG.debug("Response already committed for handling ",e);
// Complete async requests
if (request.isAsyncStarted())
request.getAsyncContext().complete();
}
finally
{

View File

@ -41,6 +41,7 @@ import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.QuietServletException;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
@ -68,7 +69,7 @@ public class AsyncContextTest
_server = new Server();
_contextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
_connector = new LocalConnector(_server);
_connector.setIdleTimeout(30000);
_connector.setIdleTimeout(5000);
_server.setConnectors(new Connector[]
{ _connector });
@ -76,6 +77,7 @@ public class AsyncContextTest
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/path with spaces/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet2()),"/servletPath2");
_contextHandler.addServlet(new ServletHolder(new TestStartThrowServlet()),"/startthrow/*");
_contextHandler.addServlet(new ServletHolder(new ForwardingServlet()),"/forward");
_contextHandler.addServlet(new ServletHolder(new AsyncDispatchingServlet()),"/dispatchingServlet");
_contextHandler.addServlet(new ServletHolder(new ExpireServlet()),"/expire/*");
@ -84,7 +86,8 @@ public class AsyncContextTest
ErrorPageErrorHandler error_handler = new ErrorPageErrorHandler();
_contextHandler.setErrorHandler(error_handler);
error_handler.addErrorPage(500,"/error");
error_handler.addErrorPage(500,"/error/500");
error_handler.addErrorPage(IOException.class.getName(),"/error/IOE");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]
@ -116,6 +119,25 @@ public class AsyncContextTest
}
@Test
public void testStartThrow() throws Exception
{
String request = "GET /ctx/startthrow HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
BufferedReader br = new BufferedReader(new StringReader(responseString));
assertEquals("HTTP/1.1 500 Server Error",br.readLine());
br.readLine();// connection close
br.readLine();// server
br.readLine();// empty
Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine());
}
@Test
public void testDispatchAsyncContext() throws Exception
{
@ -327,6 +349,7 @@ public class AsyncContextTest
br.readLine();// empty
Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
Assert.assertEquals("error servlet","PathInfo= /500",br.readLine());
Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: TEST",br.readLine());
}
@ -365,6 +388,7 @@ public class AsyncContextTest
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.getOutputStream().print("ERROR: " + request.getServletPath() + "\n");
response.getOutputStream().print("PathInfo= " + request.getPathInfo() + "\n");
if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)!=null)
response.getOutputStream().print("EXCEPTION: " + request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) + "\n");
}
@ -462,6 +486,21 @@ public class AsyncContextTest
asyncContext.start(new AsyncRunnable(asyncContext));
}
}
private class TestStartThrowServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
if (request.getDispatcherType()==DispatcherType.REQUEST)
{
request.startAsync(request, response);
throw new QuietServletException(new IOException("Test"));
}
}
}
private class AsyncRunnable implements Runnable
{

View File

@ -6,6 +6,16 @@
etc/jetty-logging.xml
[ini-template]
## STDERR / STDOUT Logging
## Logging Configuration
# Configure jetty logging for default internal behavior STDERR output
# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
# Configure jetty logging for slf4j
# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
# Configure jetty logging for java.util.logging
# -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.JavaUtilLog
# STDERR / STDOUT Logging
# Number of days to retain logs
# jetty.log.retain=90

View File

@ -35,12 +35,12 @@ public class MultiPartOutputStream extends FilterOutputStream
private static final byte[] __CRLF={'\r','\n'};
private static final byte[] __DASHDASH={'-','-'};
public static String MULTIPART_MIXED="multipart/mixed";
public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
public static final String MULTIPART_MIXED="multipart/mixed";
public static final String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
/* ------------------------------------------------------------ */
private String boundary;
private byte[] boundaryBytes;
private final String boundary;
private final byte[] boundaryBytes;
/* ------------------------------------------------------------ */
private boolean inPart=false;
@ -54,8 +54,15 @@ public class MultiPartOutputStream extends FilterOutputStream
boundary = "jetty"+System.identityHashCode(this)+
Long.toString(System.currentTimeMillis(),36);
boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
}
inPart=false;
public MultiPartOutputStream(OutputStream out, String boundary)
throws IOException
{
super(out);
this.boundary = boundary;
boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
}
/* ------------------------------------------------------------ */
@ -66,14 +73,20 @@ public class MultiPartOutputStream extends FilterOutputStream
public void close()
throws IOException
{
if (inPart)
try
{
if (inPart)
out.write(__CRLF);
out.write(__DASHDASH);
out.write(boundaryBytes);
out.write(__DASHDASH);
out.write(__CRLF);
out.write(__DASHDASH);
out.write(boundaryBytes);
out.write(__DASHDASH);
out.write(__CRLF);
inPart=false;
super.close();
inPart=false;
}
finally
{
super.close();
}
}
/* ------------------------------------------------------------ */

View File

@ -55,37 +55,31 @@ public class CounterStatistic
/**
* @param delta the amount to add to the count
*/
public void add(final long delta)
public long add(final long delta)
{
long value=_curr.addAndGet(delta);
if (delta > 0)
{
_total.addAndGet(delta);
Atomics.updateMax(_max,value);
}
/* ------------------------------------------------------------ */
/**
* @param delta the amount to subtract the count by.
*/
public void subtract(final long delta)
{
add(-delta);
Atomics.updateMax(_max,value);
}
return value;
}
/* ------------------------------------------------------------ */
/**
*/
public void increment()
public long increment()
{
add(1);
return add(1);
}
/* ------------------------------------------------------------ */
/**
*/
public void decrement()
public long decrement()
{
add(-1);
return add(-1);
}
/* ------------------------------------------------------------ */

View File

@ -154,6 +154,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private String[] _contextWhiteList = null;
private File _tmpDir;
private boolean _persistTmpDir = false;
private String _war;
private String _extraClasspath;
private Throwable _unavailableException;
@ -1222,7 +1224,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
LOG.warn(e);
}
_tmpDir=dir;
setAttribute(TEMPDIR,_tmpDir);
setAttribute(TEMPDIR,_tmpDir);
}
/* ------------------------------------------------------------ */
@ -1232,6 +1234,27 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return _tmpDir;
}
/**
* If true the temp directory for this
* webapp will be kept when the webapp stops. Otherwise,
* it will be deleted.
*
* @param delete
*/
public void setPersistTempDirectory(boolean persist)
{
_persistTmpDir = persist;
}
/**
* @return
*/
public boolean isPersistTempDirectory()
{
return _persistTmpDir;
}
/* ------------------------------------------------------------ */
/**
* @param war The war to set as a file name or URL
@ -1480,5 +1503,4 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
{
return _metadata;
}
}

View File

@ -27,6 +27,7 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
@ -57,17 +58,15 @@ public class WebInfConfiguration extends AbstractConfiguration
* resource base as a resource collection.
*/
public static final String RESOURCE_DIRS = "org.eclipse.jetty.resources";
protected Resource _preUnpackBaseResource;
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
// Look for a work directory
File work = findWorkDirectory(context);
if (work != null)
makeTempDirectory(work, context, false);
//Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);
@ -192,20 +191,16 @@ public class WebInfConfiguration extends AbstractConfiguration
@Override
public void deconfigure(WebAppContext context) throws Exception
{
// delete temp directory if we had to create it or if it isn't called work
Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
if (context.getTempDirectory()!=null && (tmpdirConfigured == null || !tmpdirConfigured.booleanValue()) && !isTempWorkDirectory(context.getTempDirectory()))
//if we're not persisting the temp dir contents delete it
if (!context.isPersistTempDirectory())
{
IO.delete(context.getTempDirectory());
context.setTempDirectory(null);
//clear out the context attributes for the tmp dir only if we had to
//create the tmp dir
context.setAttribute(TEMPDIR_CONFIGURED, null);
context.setAttribute(WebAppContext.TEMPDIR, null);
}
//if it wasn't explicitly configured by the user, then unset it
Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
if (tmpdirConfigured != null && !tmpdirConfigured)
context.setTempDirectory(null);
//reset the base resource back to what it was before we did any unpacking of resources
context.setBaseResource(_preUnpackBaseResource);
@ -238,51 +233,44 @@ public class WebInfConfiguration extends AbstractConfiguration
* <p>A. Try to use an explicit directory specifically for this webapp:</p>
* <ol>
* <li>
* Iff an explicit directory is set for this webapp, use it. Do NOT set
* delete on exit.
* Iff an explicit directory is set for this webapp, use it. Set delete on
* exit depends on value of persistTempDirectory.
* </li>
* <li>
* Iff javax.servlet.context.tempdir context attribute is set for
* this webapp && exists && writeable, then use it. Do NOT set delete on exit.
* this webapp && exists && writeable, then use it. Set delete on exit depends on
* value of persistTempDirectory.
* </li>
* </ol>
*
* <p>B. Create a directory based on global settings. The new directory
* will be called "Jetty_"+host+"_"+port+"__"+context+"_"+virtualhost
* Work out where to create this directory:
* <ol>
* <li>
* Iff $(jetty.home)/work exists create the directory there. Do NOT
* set delete on exit. Do NOT delete contents if dir already exists.
* </li>
* <li>
* Iff WEB-INF/work exists create the directory there. Do NOT set
* delete on exit. Do NOT delete contents if dir already exists.
* </li>
* <li>
* Else create dir in $(java.io.tmpdir). Set delete on exit. Delete
* contents if dir already exists.
* </li>
* </ol>
* will be called "Jetty-"+host+"-"+port+"__"+context+"-"+virtualhost+"-"+randomdigits+".dir"
* </p>
* <p>
* If the user has specified the context attribute org.eclipse.jetty.webapp.basetempdir, the
* directory specified by this attribute will be the parent of the temp dir created. Otherwise,
* the parent dir is $(java.io.tmpdir). Set delete on exit depends on value of persistTempDirectory.
* </p>
*/
public void resolveTempDirectory (WebAppContext context)
throws Exception
{
//If a tmp directory is already set, we're done
//If a tmp directory is already set we should use it
File tmpDir = context.getTempDirectory();
if (tmpDir != null && tmpDir.isDirectory() && tmpDir.canWrite())
if (tmpDir != null)
{
context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
return; // Already have a suitable tmp dir configured
configureTempDirectory(tmpDir, context);
context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE); //the tmp dir was set explicitly
return;
}
// No temp directory configured, try to establish one.
// First we check the context specific, javax.servlet specified, temp directory attribute
// No temp directory configured, try to establish one via the javax.servlet.context.tempdir.
File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
if (servletTmpDir != null && servletTmpDir.isDirectory() && servletTmpDir.canWrite())
if (servletTmpDir != null)
{
// Use as tmpDir
tmpDir = servletTmpDir;
configureTempDirectory(tmpDir, context);
// Ensure Attribute has File object
context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
// Set as TempDir in context.
@ -290,60 +278,25 @@ public class WebInfConfiguration extends AbstractConfiguration
return;
}
try
//We need to make a temp dir. Check if the user has set a directory to use instead
//of java.io.tmpdir as the parent of the dir
File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
{
// Put the tmp dir in the work directory if we had one
File work = new File(System.getProperty("jetty.base"),"work");
if (work.exists() && work.canWrite() && work.isDirectory())
{
makeTempDirectory(work, context, false); //make a tmp dir inside work, don't delete if it exists
}
else
{
File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
{
// Use baseTemp directory (allow the funky Jetty_0_0_0_0.. subdirectory logic to kick in
makeTempDirectory(baseTemp,context,false);
}
else
{
makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context,true); //make a tmpdir, delete if it already exists
}
}
//Make a temp directory as a child of the given base dir
makeTempDirectory(baseTemp,context);
}
catch(Exception e)
else
{
tmpDir=null;
LOG.ignore(e);
}
//Third ... Something went wrong trying to make the tmp directory, just make
//a jvm managed tmp directory
if (context.getTempDirectory() == null)
{
try
{
// Last resort
tmpDir=File.createTempFile("JettyContext","");
if (tmpDir.exists())
IO.delete(tmpDir);
tmpDir.mkdir();
tmpDir.deleteOnExit();
context.setTempDirectory(tmpDir);
}
catch(IOException e)
{
tmpDir = null;
throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
}
//Make a temp directory in java.io.tmpdir
makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context);
}
}
/**
* Given an Object, return File reference for object.
* Typically used to convert anonymous Object from getAttribute() calls to a File object.
* @param fileattr the file attribute to analyze and return from (supports type File and type String, all others return null)
* @param fileattr the file attribute to analyze and return from (supports type File and type String, all others return null
* @return the File object, null if null, or null if not a File or String
*/
private File asFile(Object fileattr)
@ -365,45 +318,47 @@ public class WebInfConfiguration extends AbstractConfiguration
public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
throws IOException
public void makeTempDirectory (File parent, WebAppContext context)
throws Exception
{
if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
if (parent == null || !parent.exists() || !parent.canWrite() || !parent.isDirectory())
throw new IllegalStateException("Parent for temp dir not configured correctly: "+(parent==null?"null":"writeable="+parent.canWrite()));
String temp = getCanonicalNameForWebAppTmpDir(context);
File tmpDir = File.createTempFile(temp, ".dir", parent);
//delete the file that was created
tmpDir.delete();
//and make a directory of the same name
tmpDir.mkdirs();
configureTempDirectory(tmpDir, context);
if(LOG.isDebugEnabled())
LOG.debug("Set temp dir "+tmpDir);
context.setTempDirectory(tmpDir);
}
private void configureTempDirectory (File dir, WebAppContext context)
{
if (dir == null)
throw new IllegalArgumentException("Null temp dir");
//if dir exists and we don't want it persisted, delete it
if (dir.exists() && !context.isPersistTempDirectory())
{
String temp = getCanonicalNameForWebAppTmpDir(context);
File tmpDir = new File(parent,temp);
if (deleteExisting && tmpDir.exists())
{
if (!IO.delete(tmpDir))
{
if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
}
//If we can't delete the existing tmp dir, create a new one
if (tmpDir.exists())
{
String old=tmpDir.toString();
tmpDir=File.createTempFile(temp+"_","");
if (tmpDir.exists())
IO.delete(tmpDir);
LOG.warn("Can't reuse "+old+", using "+tmpDir);
}
}
if (!tmpDir.exists())
tmpDir.mkdir();
//If the parent is not a work directory
if (!isTempWorkDirectory(tmpDir))
{
tmpDir.deleteOnExit();
}
if(LOG.isDebugEnabled())
LOG.debug("Set temp dir "+tmpDir);
context.setTempDirectory(tmpDir);
if (!IO.delete(dir))
throw new IllegalStateException("Failed to delete temp dir "+dir);
}
//if it doesn't exist make it
if (!dir.exists())
dir.mkdirs();
if (!context.isPersistTempDirectory())
dir.deleteOnExit();
//is it useable
if (!dir.canWrite() || !dir.isDirectory())
throw new IllegalStateException("Temp dir "+dir+" not useable: writeable="+dir.canWrite()+", dir="+dir.isDirectory());
}
@ -566,45 +521,17 @@ public class WebInfConfiguration extends AbstractConfiguration
}
public File findWorkDirectory (WebAppContext context) throws IOException
{
if (context.getBaseResource() != null)
{
Resource web_inf = context.getWebInf();
if (web_inf !=null && web_inf.exists())
{
return new File(web_inf.getFile(),"work");
}
}
return null;
}
/**
* Check if the tmpDir itself is called "work", or if the tmpDir
* is in a directory called "work".
* @return true if File is a temporary or work directory
*/
public boolean isTempWorkDirectory (File tmpDir)
{
if (tmpDir == null)
return false;
if (tmpDir.getName().equalsIgnoreCase("work"))
return true;
File t = tmpDir.getParentFile();
if (t == null)
return false;
return (t.getName().equalsIgnoreCase("work"));
}
/**
* Create a canonical name for a webapp temp directory.
* The form of the name is:
* <code>"Jetty_"+host+"_"+port+"__"+resourceBase+"_"+context+"_"+virtualhost+base36_hashcode_of_whole_string</code>
* <code>"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+".dir"</code>
*
* host and port uniquely identify the server
* context and virtual host uniquely identify the webapp
* randomdigits ensure every tmp directory is unique
*
* @return the canonical name for the webapp temp directory
*/
public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
@ -697,6 +624,7 @@ public class WebInfConfiguration extends AbstractConfiguration
}
canonicalName.append("-");
return canonicalName.toString();
}

View File

@ -54,7 +54,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
}
if (anno.maxBinaryMessageSize() > 0)
{
this.policy.setMaxTextMessageSize(anno.maxBinaryMessageSize());
this.policy.setMaxBinaryMessageSize(anno.maxBinaryMessageSize());
}
if (anno.inputBufferSize() > 0)
{

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.websocket.servlet;
import org.eclipse.jetty.websocket.api.extensions.Extension;
/**
* Abstract WebSocket creator interface.
* <p>