Merge branch 'jetty-7' into release-7

This commit is contained in:
Jesse McConnell 2013-07-25 07:08:49 -05:00
commit 4c805903f8
117 changed files with 1176 additions and 550 deletions

View File

@ -8,6 +8,7 @@
<artifactId>example-jetty-embedded</artifactId> <artifactId>example-jetty-embedded</artifactId>
<name>Example :: Jetty Embedded</name> <name>Example :: Jetty Embedded</name>
<description>Jetty Embedded Examples</description> <description>Jetty Embedded Examples</description>
<url>http://www.eclipse.org/jetty</url>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -8,6 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-all-server</artifactId> <artifactId>jetty-all-server</artifactId>
<name>Jetty :: Aggregate :: All Server</name> <name>Jetty :: Aggregate :: All Server</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.${project.artifactId}</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.${project.artifactId}</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-all</artifactId> <artifactId>jetty-all</artifactId>
<name>Jetty :: Aggregate :: All core Jetty</name> <name>Jetty :: Aggregate :: All core Jetty</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-client</artifactId> <artifactId>jetty-client</artifactId>
<name>Jetty :: Aggregate :: HTTP Client</name> <name>Jetty :: Aggregate :: HTTP Client</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-plus</artifactId> <artifactId>jetty-plus</artifactId>
<name>Jetty :: Aggregate :: Plus Server</name> <name>Jetty :: Aggregate :: Plus Server</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<name>Jetty :: Aggregate :: HTTP Server</name> <name>Jetty :: Aggregate :: HTTP Server</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-servlet</artifactId> <artifactId>jetty-servlet</artifactId>
<name>Jetty :: Aggregate :: Servlet Server</name> <name>Jetty :: Aggregate :: Servlet Server</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>
<name>Jetty :: Aggregate :: WebApp Server</name> <name>Jetty :: Aggregate :: WebApp Server</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-websocket</artifactId> <artifactId>jetty-websocket</artifactId>
<name>Jetty :: Aggregate :: Websocket</name> <name>Jetty :: Aggregate :: Websocket</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory> <sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins> <plugins>

View File

@ -9,6 +9,7 @@
<groupId>org.eclipse.jetty.aggregate</groupId> <groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId> <artifactId>jetty-aggregate-project</artifactId>
<name>Jetty :: Aggregate Project</name> <name>Jetty :: Aggregate Project</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>pom</packaging> <packaging>pom</packaging>
<build> <build>
<plugins> <plugins>

View File

@ -7,6 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ajp</artifactId> <artifactId>jetty-ajp</artifactId>
<name>Jetty :: AJP</name> <name>Jetty :: AJP</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.ajp</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.ajp</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-annotations</artifactId> <artifactId>jetty-annotations</artifactId>
<name>Jetty :: Servlet Annotations</name> <name>Jetty :: Servlet Annotations</name>
<description>Annotation support for deploying servlets in jetty.</description> <description>Annotation support for deploying servlets in jetty.</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.annotations</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.annotations</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,13 +8,11 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-client</artifactId> <artifactId>jetty-client</artifactId>
<name>Jetty :: Asynchronous HTTP Client</name> <name>Jetty :: Asynchronous HTTP Client</name>
<url>{$jetty.url}</url> <url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name>
<jetty.test.policy.loc>target/test-policy</jetty.test.policy.loc> <jetty.test.policy.loc>target/test-policy</jetty.test.policy.loc>
</properties> </properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -52,10 +52,10 @@ public class HttpDestination implements Dumpable
{ {
private static final Logger LOG = Log.getLogger(HttpDestination.class); private static final Logger LOG = Log.getLogger(HttpDestination.class);
private final List<HttpExchange> _queue = new LinkedList<HttpExchange>(); private final List<HttpExchange> _exchanges = new LinkedList<HttpExchange>();
private final List<AbstractHttpConnection> _connections = new LinkedList<AbstractHttpConnection>(); private final List<AbstractHttpConnection> _connections = new LinkedList<AbstractHttpConnection>();
private final BlockingQueue<Object> _newQueue = new ArrayBlockingQueue<Object>(10, true); private final BlockingQueue<Object> _reservedConnections = new ArrayBlockingQueue<Object>(10, true);
private final List<AbstractHttpConnection> _idle = new ArrayList<AbstractHttpConnection>(); private final List<AbstractHttpConnection> _idleConnections = new ArrayList<AbstractHttpConnection>();
private final HttpClient _client; private final HttpClient _client;
private final Address _address; private final Address _address;
private final boolean _ssl; private final boolean _ssl;
@ -63,14 +63,12 @@ public class HttpDestination implements Dumpable
private volatile int _maxConnections; private volatile int _maxConnections;
private volatile int _maxQueueSize; private volatile int _maxQueueSize;
private int _pendingConnections = 0; private int _pendingConnections = 0;
private int _newConnection = 0; private int _pendingReservedConnections = 0;
private volatile Address _proxy; private volatile Address _proxy;
private Authentication _proxyAuthentication; private Authentication _proxyAuthentication;
private PathMap _authorizations; private PathMap _authorizations;
private List<HttpCookie> _cookies; private List<HttpCookie> _cookies;
HttpDestination(HttpClient client, Address address, boolean ssl) HttpDestination(HttpClient client, Address address, boolean ssl)
{ {
_client = client; _client = client;
@ -136,7 +134,7 @@ public class HttpDestination implements Dumpable
{ {
synchronized (this) synchronized (this)
{ {
return _idle.size(); return _idleConnections.size();
} }
} }
@ -185,7 +183,7 @@ public class HttpDestination implements Dumpable
int totalConnections = _connections.size() + _pendingConnections; int totalConnections = _connections.size() + _pendingConnections;
if (totalConnections < _maxConnections) if (totalConnections < _maxConnections)
{ {
_newConnection++; _pendingReservedConnections++;
startConnection = true; startConnection = true;
} }
} }
@ -195,7 +193,7 @@ public class HttpDestination implements Dumpable
startNewConnection(); startNewConnection();
try try
{ {
Object o = _newQueue.take(); Object o = _reservedConnections.take();
if (o instanceof AbstractHttpConnection) if (o instanceof AbstractHttpConnection)
{ {
connection = (AbstractHttpConnection)o; connection = (AbstractHttpConnection)o;
@ -246,8 +244,8 @@ public class HttpDestination implements Dumpable
connection.close(); connection.close();
connection = null; connection = null;
} }
if (_idle.size() > 0) if (_idleConnections.size() > 0)
connection = _idle.remove(_idle.size() - 1); connection = _idleConnections.remove(_idleConnections.size() - 1);
} }
if (connection == null) if (connection == null)
@ -291,20 +289,20 @@ public class HttpDestination implements Dumpable
synchronized (this) synchronized (this)
{ {
_pendingConnections--; _pendingConnections--;
if (_newConnection > 0) if (_pendingReservedConnections > 0)
{ {
connect_failure = throwable; connect_failure = throwable;
_newConnection--; _pendingReservedConnections--;
} }
else if (_queue.size() > 0) else if (_exchanges.size() > 0)
{ {
HttpExchange ex = _queue.remove(0); HttpExchange ex = _exchanges.remove(0);
if (ex.setStatus(HttpExchange.STATUS_EXCEPTED)) if (ex.setStatus(HttpExchange.STATUS_EXCEPTED))
ex.getEventListener().onConnectionFailed(throwable); ex.getEventListener().onConnectionFailed(throwable);
// Since an existing connection had failed, we need to create a // Since an existing connection had failed, we need to create a
// connection if the queue is not empty and client is running. // connection if the queue is not empty and client is running.
if (!_queue.isEmpty() && _client.isStarted()) if (!_exchanges.isEmpty() && _client.isStarted())
startConnection = true; startConnection = true;
} }
} }
@ -316,7 +314,7 @@ public class HttpDestination implements Dumpable
{ {
try try
{ {
_newQueue.put(connect_failure); _reservedConnections.put(connect_failure);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {
@ -330,10 +328,10 @@ public class HttpDestination implements Dumpable
synchronized (this) synchronized (this)
{ {
_pendingConnections--; _pendingConnections--;
if (_queue.size() > 0) if (_exchanges.size() > 0)
{ {
HttpExchange ex = _queue.remove(0); HttpExchange ex = _exchanges.remove(0);
if(ex.setStatus(HttpExchange.STATUS_EXCEPTED)) if (ex.setStatus(HttpExchange.STATUS_EXCEPTED))
ex.getEventListener().onException(throwable); ex.getEventListener().onException(throwable);
} }
} }
@ -341,47 +339,53 @@ public class HttpDestination implements Dumpable
public void onNewConnection(final AbstractHttpConnection connection) throws IOException public void onNewConnection(final AbstractHttpConnection connection) throws IOException
{ {
Connection q_connection = null; Connection reservedConnection = null;
synchronized (this) synchronized (this)
{ {
_pendingConnections--; _pendingConnections--;
_connections.add(connection); _connections.add(connection);
if (_newConnection > 0) if (_pendingReservedConnections > 0)
{ {
q_connection = connection; reservedConnection = connection;
_newConnection--; _pendingReservedConnections--;
}
else if (_queue.size() == 0)
{
connection.setIdleTimeout();
_idle.add(connection);
} }
else else
{ {
// Establish the tunnel if needed
EndPoint endPoint = connection.getEndPoint(); EndPoint endPoint = connection.getEndPoint();
if (isProxied() && endPoint instanceof SelectConnector.UpgradableEndPoint) if (isProxied() && endPoint instanceof SelectConnector.UpgradableEndPoint)
{ {
SelectConnector.UpgradableEndPoint proxyEndPoint = (SelectConnector.UpgradableEndPoint)endPoint; SelectConnector.UpgradableEndPoint proxyEndPoint = (SelectConnector.UpgradableEndPoint)endPoint;
HttpExchange exchange = _queue.get(0); ConnectExchange connect = new ConnectExchange(getAddress(), proxyEndPoint);
ConnectExchange connect = new ConnectExchange(getAddress(), proxyEndPoint, exchange);
connect.setAddress(getProxy()); connect.setAddress(getProxy());
LOG.debug("Establishing tunnel to {} via {}", getAddress(), getProxy());
send(connection, connect); send(connection, connect);
} }
else else
{ {
HttpExchange exchange = _queue.remove(0); // Another connection stole the exchange that caused the creation of this connection ?
if (_exchanges.size() == 0)
{
LOG.debug("No exchanges for new connection {}", connection);
connection.setIdleTimeout();
_idleConnections.add(connection);
}
else
{
HttpExchange exchange = _exchanges.remove(0);
send(connection, exchange); send(connection, exchange);
} }
} }
} }
}
if (q_connection != null) if (reservedConnection != null)
{ {
try try
{ {
_newQueue.put(q_connection); _reservedConnections.put(reservedConnection);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {
@ -414,14 +418,14 @@ public class HttpDestination implements Dumpable
{ {
synchronized (this) synchronized (this)
{ {
if (_queue.size() == 0) if (_exchanges.size() == 0)
{ {
connection.setIdleTimeout(); connection.setIdleTimeout();
_idle.add(connection); _idleConnections.add(connection);
} }
else else
{ {
HttpExchange ex = _queue.remove(0); HttpExchange ex = _exchanges.remove(0);
send(connection, ex); send(connection, ex);
} }
this.notifyAll(); this.notifyAll();
@ -433,7 +437,7 @@ public class HttpDestination implements Dumpable
synchronized (this) synchronized (this)
{ {
_connections.remove(connection); _connections.remove(connection);
if (!_queue.isEmpty()) if (!_exchanges.isEmpty())
startConnection = true; startConnection = true;
} }
@ -445,16 +449,16 @@ public class HttpDestination implements Dumpable
public void returnIdleConnection(AbstractHttpConnection connection) public void returnIdleConnection(AbstractHttpConnection connection)
{ {
// TODO work out the real idle time; // TODO work out the real idle time;
long idleForMs=connection!=null&&connection.getEndPoint()!=null?connection.getEndPoint().getMaxIdleTime():-1; long idleForMs = connection.getEndPoint() != null ? connection.getEndPoint().getMaxIdleTime() : -1;
connection.onIdleExpired(idleForMs); connection.onIdleExpired(idleForMs);
boolean startConnection = false; boolean startConnection = false;
synchronized (this) synchronized (this)
{ {
_idle.remove(connection); _idleConnections.remove(connection);
_connections.remove(connection); _connections.remove(connection);
if (!_queue.isEmpty() && _client.isStarted()) if (!_exchanges.isEmpty() && _client.isStarted())
startConnection = true; startConnection = true;
} }
@ -472,10 +476,9 @@ public class HttpDestination implements Dumpable
for (int i = listeners.size(); i > 0; --i) for (int i = listeners.size(); i > 0; --i)
{ {
String listenerClass = listeners.get(i - 1); String listenerClass = listeners.get(i - 1);
try try
{ {
Class listener = Class.forName(listenerClass); Class<?> listener = Class.forName(listenerClass);
Constructor constructor = listener.getDeclaredConstructor(HttpDestination.class, HttpExchange.class); Constructor constructor = listener.getDeclaredConstructor(HttpDestination.class, HttpExchange.class);
HttpEventListener elistener = (HttpEventListener)constructor.newInstance(this, ex); HttpEventListener elistener = (HttpEventListener)constructor.newInstance(this, ex);
ex.setEventListener(elistener); ex.setEventListener(elistener);
@ -484,7 +487,9 @@ public class HttpDestination implements Dumpable
{ {
throw new IOException("Unable to instantiate registered listener for destination: " + listenerClass) throw new IOException("Unable to instantiate registered listener for destination: " + listenerClass)
{ {
{initCause(e);} {
initCause(e);
}
}; };
} }
} }
@ -549,10 +554,10 @@ public class HttpDestination implements Dumpable
boolean startConnection = false; boolean startConnection = false;
synchronized (this) synchronized (this)
{ {
if (_queue.size() == _maxQueueSize) if (_exchanges.size() == _maxQueueSize)
throw new RejectedExecutionException("Queue full for address " + _address); throw new RejectedExecutionException("Queue full for address " + _address);
_queue.add(ex); _exchanges.add(ex);
if (_connections.size() + _pendingConnections < _maxConnections) if (_connections.size() + _pendingConnections < _maxConnections)
startConnection = true; startConnection = true;
} }
@ -568,7 +573,7 @@ public class HttpDestination implements Dumpable
// destination queue, make sure it is removed // destination queue, make sure it is removed
synchronized (this) synchronized (this)
{ {
_queue.remove(exchange); _exchanges.remove(exchange);
} }
} }
@ -581,7 +586,7 @@ public class HttpDestination implements Dumpable
if (!connection.send(exchange)) if (!connection.send(exchange))
{ {
if (exchange.getStatus() <= HttpExchange.STATUS_WAITING_FOR_CONNECTION) if (exchange.getStatus() <= HttpExchange.STATUS_WAITING_FOR_CONNECTION)
_queue.add(0, exchange); _exchanges.add(0, exchange);
returnIdleConnection(connection); returnIdleConnection(connection);
} }
} }
@ -590,7 +595,7 @@ public class HttpDestination implements Dumpable
@Override @Override
public synchronized String toString() public synchronized String toString()
{ {
return String.format("HttpDestination@%x//%s:%d(%d/%d,%d,%d/%d)%n",hashCode(),_address.getHost(),_address.getPort(),_connections.size(),_maxConnections,_idle.size(),_queue.size(),_maxQueueSize); return String.format("HttpDestination@%x//%s:%d(%d/%d,%d,%d/%d)%n", hashCode(), _address.getHost(), _address.getPort(), _connections.size(), _maxConnections, _idleConnections.size(), _exchanges.size(), _maxQueueSize);
} }
public synchronized String toDetailString() public synchronized String toDetailString()
@ -603,7 +608,7 @@ public class HttpDestination implements Dumpable
for (AbstractHttpConnection connection : _connections) for (AbstractHttpConnection connection : _connections)
{ {
b.append(connection.toDetailString()); b.append(connection.toDetailString());
if (_idle.contains(connection)) if (_idleConnections.contains(connection))
b.append(" IDLE"); b.append(" IDLE");
b.append('\n'); b.append('\n');
} }
@ -650,39 +655,33 @@ public class HttpDestination implements Dumpable
} }
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.Dumpable#dump()
*/
public String dump() public String dump()
{ {
return AggregateLifeCycle.dump(this); return AggregateLifeCycle.dump(this);
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.Dumpable#dump(java.lang.Appendable, java.lang.String)
*/
public void dump(Appendable out, String indent) throws IOException public void dump(Appendable out, String indent) throws IOException
{ {
synchronized (this) synchronized (this)
{ {
out.append(String.valueOf(this)+"idle="+_idle.size()+" pending="+_pendingConnections).append("\n"); out.append(String.valueOf(this));
AggregateLifeCycle.dump(out,indent,_connections); out.append("idle=");
out.append(String.valueOf(_idleConnections.size()));
out.append(" pending=");
out.append(String.valueOf(_pendingConnections));
out.append("\n");
AggregateLifeCycle.dump(out, indent, _connections);
} }
} }
private class ConnectExchange extends ContentExchange private class ConnectExchange extends ContentExchange
{ {
private final SelectConnector.UpgradableEndPoint proxyEndPoint; private final SelectConnector.UpgradableEndPoint proxyEndPoint;
private final HttpExchange exchange;
public ConnectExchange(Address serverAddress, SelectConnector.UpgradableEndPoint proxyEndPoint, HttpExchange exchange) public ConnectExchange(Address serverAddress, SelectConnector.UpgradableEndPoint proxyEndPoint)
{ {
this.proxyEndPoint = proxyEndPoint; this.proxyEndPoint = proxyEndPoint;
this.exchange = exchange;
setMethod(HttpMethods.CONNECT); setMethod(HttpMethods.CONNECT);
setVersion(exchange.getVersion());
String serverHostAndPort = serverAddress.toString(); String serverHostAndPort = serverAddress.toString();
setRequestURI(serverHostAndPort); setRequestURI(serverHostAndPort);
addRequestHeader(HttpHeaders.HOST, serverHostAndPort); addRequestHeader(HttpHeaders.HOST, serverHostAndPort);
@ -698,13 +697,13 @@ public class HttpDestination implements Dumpable
{ {
proxyEndPoint.upgrade(); proxyEndPoint.upgrade();
} }
else if(responseStatus == HttpStatus.GATEWAY_TIMEOUT_504) else if (responseStatus == HttpStatus.GATEWAY_TIMEOUT_504)
{ {
onExpire(); onExpire();
} }
else else
{ {
onException(new ProtocolException("Proxy: " + proxyEndPoint.getRemoteAddr() +":" + proxyEndPoint.getRemotePort() + " didn't return http return code 200, but " + responseStatus + " while trying to request: " + exchange.getAddress().toString())); onException(new ProtocolException("Proxy: " + proxyEndPoint.getRemoteAddr() + ":" + proxyEndPoint.getRemotePort() + " didn't return http return code 200, but " + responseStatus));
} }
} }
@ -717,18 +716,27 @@ public class HttpDestination implements Dumpable
@Override @Override
protected void onException(Throwable x) protected void onException(Throwable x)
{ {
_queue.remove(exchange); HttpExchange exchange = null;
if (exchange.setStatus(STATUS_EXCEPTED)) synchronized (HttpDestination.this)
{
if (!_exchanges.isEmpty())
exchange = _exchanges.remove(0);
}
if (exchange != null && exchange.setStatus(STATUS_EXCEPTED))
exchange.getEventListener().onException(x); exchange.getEventListener().onException(x);
} }
@Override @Override
protected void onExpire() protected void onExpire()
{ {
_queue.remove(exchange); HttpExchange exchange = null;
if (exchange.setStatus(STATUS_EXPIRED)) synchronized (HttpDestination.this)
{
if (!_exchanges.isEmpty())
exchange = _exchanges.remove(0);
}
if (exchange != null && exchange.setStatus(STATUS_EXPIRED))
exchange.getEventListener().onExpire(); exchange.getEventListener().onExpire();
} }
} }
} }

View File

@ -125,10 +125,10 @@ public class HttpExchange
protected void expire(HttpDestination destination) protected void expire(HttpDestination destination)
{ {
AbstractHttpConnection connection = _connection;
if (getStatus() < HttpExchange.STATUS_COMPLETED) if (getStatus() < HttpExchange.STATUS_COMPLETED)
setStatus(HttpExchange.STATUS_EXPIRED); setStatus(HttpExchange.STATUS_EXPIRED);
destination.exchangeExpired(this); destination.exchangeExpired(this);
AbstractHttpConnection connection = _connection;
if (connection != null) if (connection != null)
connection.exchangeExpired(this); connection.exchangeExpired(this);
} }

View File

@ -18,13 +18,28 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/** /**
* IdleTimeoutTest * IdleTimeoutTest
* *
@ -77,4 +92,56 @@ public class IdleTimeoutTest
Assert.fail("Test did not complete in time"); Assert.fail("Test did not complete in time");
} }
@Test
public void testConnectionsAreReleasedWhenExpired() throws Exception
{
// we need a server that times out and a client with shorter timeout settings, so we need to create new ones
Server server = new Server();
Connector connector = new SelectChannelConnector();
server.addConnector(connector);
server.setHandler(new AbstractHandler()
{
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (request.getParameter("timeout") != null)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
baseRequest.setHandled(true);
response.getWriter().write("Hello world");
}
});
server.start();
HttpClient httpClient = new HttpClient();
httpClient.setMaxConnectionsPerAddress(1);
httpClient.setConnectTimeout(200);
httpClient.setTimeout(200);
httpClient.setIdleTimeout(200);
httpClient.start();
String uriString = "http://localhost:" + connector.getLocalPort() + "/";
HttpExchange httpExchange = new HttpExchange();
httpExchange.setURI(URI.create(uriString).resolve("?timeout=true"));
httpExchange.setMethod(HttpMethods.GET);
httpClient.send(httpExchange);
int status = httpExchange.waitForDone();
assertThat("First request expired", status, is(8));
httpExchange = new HttpExchange();
httpExchange.setURI(URI.create(uriString));
httpExchange.setMethod(HttpMethods.GET);
httpClient.send(httpExchange);
status = httpExchange.waitForDone();
assertThat("Second request was successful as timeout is not set", status, is(7));
}
} }

View File

@ -18,13 +18,12 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.Assert.*;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -32,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
@ -45,8 +45,12 @@ import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ProxyTunnellingTest public class ProxyTunnellingTest
{ {
private Server server; private Server server;
@ -113,7 +117,7 @@ public class ProxyTunnellingTest
} }
@Test @Test
public void testOneMessageSSL() throws Exception public void testOneExchangeViaSSL() throws Exception
{ {
startSSLServer(new ServerHandler()); startSSLServer(new ServerHandler());
startProxy(); startProxy();
@ -141,7 +145,7 @@ public class ProxyTunnellingTest
} }
@Test @Test
public void testTwoMessagesSSL() throws Exception public void testTwoExchangesViaSSL() throws Exception
{ {
startSSLServer(new ServerHandler()); startSSLServer(new ServerHandler());
startProxy(); startProxy();
@ -181,6 +185,85 @@ public class ProxyTunnellingTest
} }
} }
@Test
public void testTwoConcurrentExchangesViaSSL() throws Exception
{
startSSLServer(new ServerHandler());
startProxy();
final HttpClient httpClient = new HttpClient();
httpClient.setProxy(new Address("localhost", proxyPort()));
httpClient.start();
try
{
final AtomicReference<AbstractHttpConnection> connection = new AtomicReference<AbstractHttpConnection>();
final CountDownLatch connectionLatch = new CountDownLatch(1);
ContentExchange exchange1 = new ContentExchange(true)
{
@Override
protected void onRequestCommitted() throws IOException
{
// Simulate the concurrent send of a second exchange which
// triggers the opening of a second connection but then
// it's "stolen" by the first connection, so that the
// second connection is put into the idle connections.
HttpDestination destination = httpClient.getDestination(new Address("localhost", serverConnector.getLocalPort()), true);
destination.startNewConnection();
// Wait until we have the new connection
AbstractHttpConnection httpConnection = null;
while (httpConnection == null)
{
try
{
Thread.sleep(10);
httpConnection = destination.getIdleConnection();
}
catch (InterruptedException x)
{
throw new InterruptedIOException();
}
}
connection.set(httpConnection);
connectionLatch.countDown();
}
};
exchange1.setMethod(HttpMethods.GET);
String body1 = "BODY";
exchange1.setURL("https://localhost:" + serverConnector.getLocalPort() + "/echo?body=" + URLEncoder.encode(body1, "UTF-8"));
httpClient.send(exchange1);
assertEquals(HttpExchange.STATUS_COMPLETED, exchange1.waitForDone());
assertEquals(HttpStatus.OK_200, exchange1.getResponseStatus());
String content1 = exchange1.getResponseContent();
assertEquals(body1, content1);
Assert.assertTrue(connectionLatch.await(5, TimeUnit.SECONDS));
ContentExchange exchange2 = new ContentExchange(true);
exchange2.setMethod(HttpMethods.POST);
exchange2.setURL("https://localhost:" + serverConnector.getLocalPort() + "/echo");
exchange2.setRequestHeader(HttpHeaders.CONTENT_TYPE, MimeTypes.FORM_ENCODED);
String body2 = "body=" + body1;
exchange2.setRequestHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(body2.length()));
exchange2.setRequestContent(new ByteArrayBuffer(body2, "UTF-8"));
// Make sure the second connection can send the exchange via the tunnel
connection.get().send(exchange2);
assertEquals(HttpExchange.STATUS_COMPLETED, exchange2.waitForDone());
assertEquals(HttpStatus.OK_200, exchange2.getResponseStatus());
String content2 = exchange2.getResponseContent();
assertEquals(body1, content2);
}
finally
{
httpClient.stop();
}
}
@Test @Test
public void testProxyDown() throws Exception public void testProxyDown() throws Exception
{ {

View File

@ -8,6 +8,7 @@
<artifactId>jetty-continuation</artifactId> <artifactId>jetty-continuation</artifactId>
<name>Jetty :: Continuation</name> <name>Jetty :: Continuation</name>
<description>Asynchronous API</description> <description>Asynchronous API</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.continuation</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.continuation</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-deploy</artifactId> <artifactId>jetty-deploy</artifactId>
<name>Jetty :: Deployers</name> <name>Jetty :: Deployers</name>
<description>Jetty deployers</description> <description>Jetty deployers</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.deploy</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.deploy</bundle-symbolic-name>
</properties> </properties>

View File

@ -25,10 +25,12 @@ import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
import org.eclipse.jetty.toolchain.test.OS; import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.After; import org.junit.After;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Before; import org.junit.Before;
@ -43,6 +45,9 @@ public class ScanningAppProviderRuntimeUpdatesTest
{ {
private static final Logger LOG = Log.getLogger(ScanningAppProviderRuntimeUpdatesTest.class); private static final Logger LOG = Log.getLogger(ScanningAppProviderRuntimeUpdatesTest.class);
@Rule
public TestTracker tracker = new TestTracker();
@Rule @Rule
public TestingDir testdir = new TestingDir(); public TestingDir testdir = new TestingDir();
private static XmlConfiguredJetty jetty; private static XmlConfiguredJetty jetty;
@ -52,6 +57,9 @@ public class ScanningAppProviderRuntimeUpdatesTest
@Before @Before
public void setupEnvironment() throws Exception public void setupEnvironment() throws Exception
{ {
testdir.ensureEmpty();
Resource.setDefaultUseCaches(false);
jetty = new XmlConfiguredJetty(testdir); jetty = new XmlConfiguredJetty(testdir);
jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty.xml");
jetty.addConfiguration("jetty-deploymgr-contexts.xml"); jetty.addConfiguration("jetty-deploymgr-contexts.xml");
@ -90,7 +98,7 @@ public class ScanningAppProviderRuntimeUpdatesTest
public void waitForDirectoryScan() public void waitForDirectoryScan()
{ {
int scan=_scans.get()+2*_providers; int scan=_scans.get()+(2*_providers);
do do
{ {
try try
@ -167,8 +175,8 @@ public class ScanningAppProviderRuntimeUpdatesTest
waitForDirectoryScan(); waitForDirectoryScan();
System.out.println("Updating war files"); System.out.println("Updating war files");
jetty.copyContext("foo.xml","foo.xml"); // essentially "touch" the context xml
jetty.copyWebapp("foo-webapp-2.war","foo.war"); jetty.copyWebapp("foo-webapp-2.war","foo.war");
jetty.copyContext("foo.xml","foo.xml"); // essentially "touch" the context xml
// This should result in the existing foo.war being replaced with the new foo.war // This should result in the existing foo.war being replaced with the new foo.war
waitForDirectoryScan(); waitForDirectoryScan();

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.deploy.test; package org.eclipse.jetty.deploy.test;
import static org.hamcrest.Matchers.*;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -213,6 +215,7 @@ public class XmlConfiguredJetty
if (context.getContextPath().equals(expectedPath)) if (context.getContextPath().equals(expectedPath))
{ {
found = true; found = true;
Assert.assertThat("Context[" + context.getContextPath() + "].state", context.getState(), is("STARTED"));
break; break;
} }
} }

View File

@ -7,6 +7,7 @@
</parent> </parent>
<artifactId>jetty-distribution</artifactId> <artifactId>jetty-distribution</artifactId>
<name>Jetty :: Distribution Assemblies</name> <name>Jetty :: Distribution Assemblies</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<assembly-directory>target/distribution</assembly-directory> <assembly-directory>target/distribution</assembly-directory>

View File

@ -7,6 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http-spi</artifactId> <artifactId>jetty-http-spi</artifactId>
<name>Jetty :: Http Service Provider Interface</name> <name>Jetty :: Http Service Provider Interface</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.http.spi</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.http.spi</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http</artifactId> <artifactId>jetty-http</artifactId>
<name>Jetty :: Http Utility</name> <name>Jetty :: Http Utility</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.http</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.http</bundle-symbolic-name>
</properties> </properties>

View File

@ -1015,14 +1015,17 @@ public class HttpGenerator extends AbstractGenerator
// If we need EOC and everything written // If we need EOC and everything written
if (_needEOC && (_content == null || _content.length() == 0)) if (_needEOC && (_content == null || _content.length() == 0))
{ {
if (_header == null && _buffer == null)
_header = _buffers.getHeader();
if (_needCRLF) if (_needCRLF)
{ {
if (_buffer == null && _header != null && _header.space() >= 2) if (_buffer == null && _header != null && _header.space() >= HttpTokens.CRLF.length)
{ {
_header.put(HttpTokens.CRLF); _header.put(HttpTokens.CRLF);
_needCRLF = false; _needCRLF = false;
} }
else if (_buffer!=null && _buffer.space() >= 2) else if (_buffer!=null && _buffer.space() >= HttpTokens.CRLF.length)
{ {
_buffer.put(HttpTokens.CRLF); _buffer.put(HttpTokens.CRLF);
_needCRLF = false; _needCRLF = false;

View File

@ -65,22 +65,31 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
doCompress(); doCompress();
} }
/* ------------------------------------------------------------ */
/** /**
* Reset buffer. * Reset buffer.
*/ */
public void resetBuffer() public void resetBuffer()
{ {
if (_response.isCommitted()) if (_response.isCommitted() || _compressedOutputStream!=null )
throw new IllegalStateException("Committed"); throw new IllegalStateException("Committed");
_closed = false; _closed = false;
_out = null; _out = null;
_bOut = null; _bOut = null;
if (_compressedOutputStream != null)
_response.setHeader("Content-Encoding",null);
_compressedOutputStream = null;
_doNotCompress = false; _doNotCompress = false;
} }
/* ------------------------------------------------------------ */
public void setBufferSize(int bufferSize)
{
if (_bOut!=null && _bOut.getBuf().length<bufferSize)
{
ByteArrayOutputStream2 b = new ByteArrayOutputStream2(bufferSize);
b.write(_bOut.getBuf(),0,_bOut.size());
_bOut=b;
}
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public void setContentLength() public void setContentLength()
{ {
@ -170,7 +179,7 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
if (_out == null || _bOut != null) if (_out == null || _bOut != null)
{ {
long length=_wrapper.getContentLength(); long length=_wrapper.getContentLength();
if (length > 0 && length < _wrapper.getMinCompressSize()) if (length >= 0 && length < _wrapper.getMinCompressSize())
doNotCompress(false); doNotCompress(false);
else else
doCompress(); doCompress();
@ -299,23 +308,39 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
if (_out == null) if (_out == null)
{ {
// If this first write is larger than buffer size, then we are committing now
if (lengthToWrite>_wrapper.getBufferSize())
{
// if we know this is all the content and it is less than minimum, then do not compress, otherwise do compress
long length=_wrapper.getContentLength(); long length=_wrapper.getContentLength();
if (_response.isCommitted() || (length >= 0 && length < _wrapper.getMinCompressSize())) if (length>=0 && length<_wrapper.getMinCompressSize())
doNotCompress(false); doNotCompress(false); // Not compressing by size, so no vary on request headers
else if (lengthToWrite > _wrapper.getMinCompressSize())
doCompress();
else else
doCompress();
}
else
{
// start aggregating writes into a buffered output stream
_out = _bOut = new ByteArrayOutputStream2(_wrapper.getBufferSize()); _out = _bOut = new ByteArrayOutputStream2(_wrapper.getBufferSize());
} }
else if (_bOut != null) }
// else are we aggregating writes?
else if (_bOut !=null)
{ {
// We are aggregating into the buffered output stream.
// If this write fills the buffer, then we are committing
if (lengthToWrite>=(_bOut.getBuf().length - _bOut.getCount()))
{
// if we know this is all the content and it is less than minimum, then do not compress, otherwise do compress
long length=_wrapper.getContentLength(); long length=_wrapper.getContentLength();
if (_response.isCommitted() || (length >= 0 && length < _wrapper.getMinCompressSize())) if (length>=0 && length<_wrapper.getMinCompressSize())
doNotCompress(false); doNotCompress(false); // Not compressing by size, so no vary on request headers
else if (lengthToWrite >= (_bOut.getBuf().length - _bOut.getCount())) else
doCompress(); doCompress();
} }
} }
}
/** /**
* @see org.eclipse.jetty.http.gzip.CompressedStream#getOutputStream() * @see org.eclipse.jetty.http.gzip.CompressedStream#getOutputStream()
@ -359,4 +384,5 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
*/ */
protected abstract DeflaterOutputStream createStream() throws IOException; protected abstract DeflaterOutputStream createStream() throws IOException;
} }

View File

@ -107,6 +107,8 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
public void setBufferSize(int bufferSize) public void setBufferSize(int bufferSize)
{ {
_bufferSize = bufferSize; _bufferSize = bufferSize;
if (_compressedStream!=null)
_compressedStream.setBufferSize(bufferSize);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -127,6 +129,8 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
{ {
super.setContentType(ct); super.setContentType(ct);
if (!_noCompression)
{
if (ct!=null) if (ct!=null)
{ {
int colon=ct.indexOf(";"); int colon=ct.indexOf(";");
@ -141,6 +145,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
noCompression(); noCompression();
} }
} }
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
@ -173,6 +178,9 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
@Override @Override
public void setContentLength(int length) public void setContentLength(int length)
{ {
if (_noCompression)
super.setContentLength(length);
else
setContentLength((long)length); setContentLength((long)length);
} }
@ -237,7 +245,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
if (_writer!=null) if (_writer!=null)
_writer.flush(); _writer.flush();
if (_compressedStream!=null) if (_compressedStream!=null)
_compressedStream.finish(); _compressedStream.flush();
else else
getResponse().flushBuffer(); getResponse().flushBuffer();
} }
@ -311,6 +319,8 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
*/ */
public void noCompression() public void noCompression()
{ {
if (!_noCompression)
setDeferredHeaders();
_noCompression=true; _noCompression=true;
if (_compressedStream!=null) if (_compressedStream!=null)
{ {
@ -335,6 +345,25 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
_writer.flush(); _writer.flush();
if (_compressedStream!=null) if (_compressedStream!=null)
_compressedStream.finish(); _compressedStream.finish();
else
setDeferredHeaders();
}
/* ------------------------------------------------------------ */
private void setDeferredHeaders()
{
if (!isCommitted())
{
if (_contentLength>=0)
{
if (_contentLength < Integer.MAX_VALUE)
super.setContentLength((int)_contentLength);
else
super.setHeader("Content-Length",Long.toString(_contentLength));
}
if(_etag!=null)
super.setHeader("ETag",_etag);
}
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -344,7 +373,9 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
@Override @Override
public void setHeader(String name, String value) public void setHeader(String name, String value)
{ {
if ("content-length".equalsIgnoreCase(name)) if (_noCompression)
super.setHeader(name,value);
else if ("content-length".equalsIgnoreCase(name))
{ {
setContentLength(Long.parseLong(value)); setContentLength(Long.parseLong(value));
} }
@ -370,7 +401,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
@Override @Override
public boolean containsHeader(String name) public boolean containsHeader(String name)
{ {
if ("etag".equalsIgnoreCase(name) && _etag!=null) if (!_noCompression && "etag".equalsIgnoreCase(name) && _etag!=null)
return true; return true;
return super.containsHeader(name); return super.containsHeader(name);
} }
@ -385,10 +416,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
if (_compressedStream==null) if (_compressedStream==null)
{ {
if (getResponse().isCommitted() || _noCompression) if (getResponse().isCommitted() || _noCompression)
{
setContentLength(_contentLength);
return getResponse().getOutputStream(); return getResponse().getOutputStream();
}
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse()); _compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
} }
@ -411,10 +439,7 @@ public abstract class CompressedResponseWrapper extends HttpServletResponseWrapp
throw new IllegalStateException("getOutputStream() called"); throw new IllegalStateException("getOutputStream() called");
if (getResponse().isCommitted() || _noCompression) if (getResponse().isCommitted() || _noCompression)
{
setContentLength(_contentLength);
return getResponse().getWriter(); return getResponse().getWriter();
}
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse()); _compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
_writer=newWriter(_compressedStream,getCharacterEncoding()); _writer=newWriter(_compressedStream,getCharacterEncoding());

View File

@ -7,6 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-io</artifactId> <artifactId>jetty-io</artifactId>
<name>Jetty :: IO Utility</name> <name>Jetty :: IO Utility</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.io</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.io</bundle-symbolic-name>
</properties> </properties>

View File

@ -31,6 +31,9 @@ import java.nio.channels.WritableByteChannel;
import org.eclipse.jetty.io.AbstractBuffer; import org.eclipse.jetty.io.AbstractBuffer;
import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**
@ -39,6 +42,8 @@ import org.eclipse.jetty.io.Buffer;
*/ */
public class DirectNIOBuffer extends AbstractBuffer implements NIOBuffer public class DirectNIOBuffer extends AbstractBuffer implements NIOBuffer
{ {
private static final Logger LOG = Log.getLogger(DirectNIOBuffer.class);
protected final ByteBuffer _buf; protected final ByteBuffer _buf;
private ReadableByteChannel _in; private ReadableByteChannel _in;
private InputStream _inStream; private InputStream _inStream;
@ -69,13 +74,23 @@ public class DirectNIOBuffer extends AbstractBuffer implements NIOBuffer
public DirectNIOBuffer(File file) throws IOException public DirectNIOBuffer(File file) throws IOException
{ {
super(READONLY,NON_VOLATILE); super(READONLY,NON_VOLATILE);
FileInputStream fis = new FileInputStream(file); FileInputStream fis = null;
FileChannel fc = fis.getChannel(); FileChannel fc = null;
try
{
fis = new FileInputStream(file);
fc = fis.getChannel();
_buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); _buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
setGetIndex(0); setGetIndex(0);
setPutIndex((int)file.length()); setPutIndex((int)file.length());
_access=IMMUTABLE; _access=IMMUTABLE;
} }
finally
{
if (fc != null) try {fc.close();} catch (IOException e){LOG.ignore(e);}
IO.close(fis);
}
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public boolean isDirect() public boolean isDirect()

View File

@ -8,6 +8,7 @@
<artifactId>jetty-jaspi</artifactId> <artifactId>jetty-jaspi</artifactId>
<name>Jetty :: JASPI Security</name> <name>Jetty :: JASPI Security</name>
<description>Jetty security infrastructure</description> <description>Jetty security infrastructure</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.jaspi</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.jaspi</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-jmx</artifactId> <artifactId>jetty-jmx</artifactId>
<name>Jetty :: JMX Management</name> <name>Jetty :: JMX Management</name>
<description>JMX management artifact for jetty.</description> <description>JMX management artifact for jetty.</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.jmx</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.jmx</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-jndi</artifactId> <artifactId>jetty-jndi</artifactId>
<name>Jetty :: JNDI Naming</name> <name>Jetty :: JNDI Naming</name>
<description>JNDI spi impl for java namespace.</description> <description>JNDI spi impl for java namespace.</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.jndi</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.jndi</bundle-symbolic-name>
</properties> </properties>

View File

@ -7,6 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jsp</artifactId> <artifactId>jetty-jsp</artifactId>
<name>Jetty :: JSP dependencies</name> <name>Jetty :: JSP dependencies</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>jar</packaging> <packaging>jar</packaging>
<build> <build>
</build> </build>

View File

@ -24,6 +24,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-monitor</artifactId> <artifactId>jetty-monitor</artifactId>
<name>Jetty :: Monitoring</name> <name>Jetty :: Monitoring</name>
<url>http://www.eclipse.org/jetty</url>
<description>Performance monitoring artifact for jetty.</description> <description>Performance monitoring artifact for jetty.</description>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.monitor</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.monitor</bundle-symbolic-name>

View File

@ -10,6 +10,7 @@
<name>Jetty :: Nested</name> <name>Jetty :: Nested</name>
<packaging>jar</packaging> <packaging>jar</packaging>
<description>Local Servlet Connector for jetty.</description> <description>Local Servlet Connector for jetty.</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.nested</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.nested</bundle-symbolic-name>
</properties> </properties>

View File

@ -7,6 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-nosql</artifactId> <artifactId>jetty-nosql</artifactId>
<name>Jetty :: NoSQL Session Managers</name> <name>Jetty :: NoSQL Session Managers</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.nosql</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.nosql</bundle-symbolic-name>
</properties> </properties>

View File

@ -40,6 +40,7 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
private int _savePeriod=0; private int _savePeriod=0;
private int _idlePeriod=-1; private int _idlePeriod=-1;
private boolean _invalidateOnStop; private boolean _invalidateOnStop;
private boolean _preserveOnStop;
private boolean _saveAllAttributes; private boolean _saveAllAttributes;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -104,9 +105,12 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
for (NoSqlSession session : sessions) for (NoSqlSession session : sessions)
{ {
session.save(false); session.save(false);
if (!_preserveOnStop) {
removeSession(session,false); removeSession(session,false);
} }
} }
}
else else
{ {
for (NoSqlSession session : sessions) for (NoSqlSession session : sessions)
@ -277,6 +281,16 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
return _invalidateOnStop; return _invalidateOnStop;
} }
/* ------------------------------------------------------------ */
/**
* Preserve sessions when the session manager is stopped otherwise remove them from the DB.
* @return the removeOnStop
*/
public boolean isPreserveOnStop()
{
return _preserveOnStop;
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* Invalidate sessions when the session manager is stopped otherwise save them to the DB. * Invalidate sessions when the session manager is stopped otherwise save them to the DB.
@ -287,6 +301,16 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
_invalidateOnStop = invalidateOnStop; _invalidateOnStop = invalidateOnStop;
} }
/* ------------------------------------------------------------ */
/**
* Preserve sessions when the session manager is stopped otherwise remove them from the DB.
* @param removeOnStop the removeOnStop to set
*/
public void setPreserveOnStop(boolean preserveOnStop)
{
_preserveOnStop = preserveOnStop;
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* Save all attributes of a session or only update the dirty attributes. * Save all attributes of a session or only update the dirty attributes.

View File

@ -131,6 +131,9 @@ public class MongoSessionManager extends NoSqlSessionManager
BasicDBObject sets = new BasicDBObject(); BasicDBObject sets = new BasicDBObject();
BasicDBObject unsets = new BasicDBObject(); BasicDBObject unsets = new BasicDBObject();
// handle valid or invalid
if (session.isValid())
{
// handle new or existing // handle new or existing
if (version == null) if (version == null)
{ {
@ -147,9 +150,6 @@ public class MongoSessionManager extends NoSqlSessionManager
update.put("$inc",__version_1); update.put("$inc",__version_1);
} }
// handle valid or invalid
if (session.isValid())
{
sets.put(__ACCESSED,session.getAccessed()); sets.put(__ACCESSED,session.getAccessed());
Set<String> names = session.takeDirty(); Set<String> names = session.takeDirty();
if (isSaveAllAttributes() || upsert) if (isSaveAllAttributes() || upsert)
@ -247,6 +247,7 @@ public class MongoSessionManager extends NoSqlSessionManager
DBObject attrs = (DBObject)getNestedValue(o,getContextKey()); DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
if (attrs != null) if (attrs != null)
{ {
for (String name : attrs.keySet()) for (String name : attrs.keySet())
@ -280,6 +281,22 @@ public class MongoSessionManager extends NoSqlSessionManager
} }
} }
/*
* We are refreshing so we should update the last accessed time.
*/
BasicDBObject key = new BasicDBObject(__ID,session.getClusterId());
BasicDBObject sets = new BasicDBObject();
// Form updates
BasicDBObject update = new BasicDBObject();
sets.put(__ACCESSED,System.currentTimeMillis());
// Do the upsert
if (!sets.isEmpty())
{
update.put("$set",sets);
}
_sessions.update(key,update,false,false);
session.didActivate(); session.didActivate();
return version; return version;

View File

@ -9,6 +9,7 @@
<artifactId>jetty-osgi-boot-jsp</artifactId> <artifactId>jetty-osgi-boot-jsp</artifactId>
<name>Jetty :: OSGi :: Boot JSP</name> <name>Jetty :: OSGi :: Boot JSP</name>
<description>Jetty OSGi Boot JSP bundle</description> <description>Jetty OSGi Boot JSP bundle</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.boot.jsp</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.boot.jsp</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>jetty-osgi-boot-logback</artifactId> <artifactId>jetty-osgi-boot-logback</artifactId>
<name>Jetty :: OSGi :: Boot Logback</name> <name>Jetty :: OSGi :: Boot Logback</name>
<description>Jetty OSGi Boot Logback bundle</description> <description>Jetty OSGi Boot Logback bundle</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.boot.logback</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.boot.logback</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>jetty-osgi-boot-warurl</artifactId> <artifactId>jetty-osgi-boot-warurl</artifactId>
<name>Jetty :: OSGi :: Boot :: Warurl</name> <name>Jetty :: OSGi :: Boot :: Warurl</name>
<description>Jetty OSGi Boot-Warurl bundle</description> <description>Jetty OSGi Boot-Warurl bundle</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.boot.warurl</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.boot.warurl</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>jetty-osgi-boot</artifactId> <artifactId>jetty-osgi-boot</artifactId>
<name>Jetty :: OSGi :: Boot</name> <name>Jetty :: OSGi :: Boot</name>
<description>Jetty OSGi Boot bundle</description> <description>Jetty OSGi Boot bundle</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.boot</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.boot</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>jetty-osgi-equinoxtools</artifactId> <artifactId>jetty-osgi-equinoxtools</artifactId>
<name>Jetty :: OSGi :: Example Equinox Tools</name> <name>Jetty :: OSGi :: Example Equinox Tools</name>
<description>Jetty OSGi Example Equinox Tools</description> <description>Jetty OSGi Example Equinox Tools</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.equinoxtools</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.equinoxtools</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>jetty-httpservice</artifactId> <artifactId>jetty-httpservice</artifactId>
<name>Jetty :: OSGi :: HttpService</name> <name>Jetty :: OSGi :: HttpService</name>
<description>Jetty OSGi HttpService bundle</description> <description>Jetty OSGi HttpService bundle</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.httpservice</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.httpservice</bundle-symbolic-name>
</properties> </properties>

View File

@ -10,6 +10,7 @@
<artifactId>jetty-osgi-servletbridge</artifactId> <artifactId>jetty-osgi-servletbridge</artifactId>
<name>Jetty :: OSGi :: Servletbridge</name> <name>Jetty :: OSGi :: Servletbridge</name>
<description>Jetty OSGi Servletbridge webapp</description> <description>Jetty OSGi Servletbridge webapp</description>
<url>http://www.eclipse.org/jetty</url>
<packaging>war</packaging> <packaging>war</packaging>
<properties><eclipse.pde>false</eclipse.pde></properties> <properties><eclipse.pde>false</eclipse.pde></properties>
<dependencies> <dependencies>

View File

@ -9,6 +9,7 @@
<groupId>org.eclipse.jetty.osgi</groupId> <groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId> <artifactId>jetty-osgi-project</artifactId>
<name>Jetty :: OSGi</name> <name>Jetty :: OSGi</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<osgi-version>3.6.0.v20100517</osgi-version> <osgi-version>3.6.0.v20100517</osgi-version>

View File

@ -9,6 +9,7 @@
<artifactId>test-jetty-osgi-context</artifactId> <artifactId>test-jetty-osgi-context</artifactId>
<name>Jetty :: OSGi :: Context</name> <name>Jetty :: OSGi :: Context</name>
<description>Test Jetty OSGi bundle with a ContextHandler</description> <description>Test Jetty OSGi bundle with a ContextHandler</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.testcontext</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.testcontext</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>test-jetty-osgi-webapp</artifactId> <artifactId>test-jetty-osgi-webapp</artifactId>
<name>Jetty :: OSGi :: WebApp</name> <name>Jetty :: OSGi :: WebApp</name>
<description>Test Jetty OSGi Webapp bundle</description> <description>Test Jetty OSGi Webapp bundle</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name>
</properties> </properties>

View File

@ -9,6 +9,7 @@
<artifactId>test-jetty-osgi</artifactId> <artifactId>test-jetty-osgi</artifactId>
<name>Jetty :: OSGi :: Test</name> <name>Jetty :: OSGi :: Test</name>
<description>Jetty OSGi Integration test</description> <description>Jetty OSGi Integration test</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.boot.test</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.boot.test</bundle-symbolic-name>
<assembly-directory>target/distribution</assembly-directory> <assembly-directory>target/distribution</assembly-directory>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-overlay-deployer</artifactId> <artifactId>jetty-overlay-deployer</artifactId>
<name>Jetty :: Overlay Deployer</name> <name>Jetty :: Overlay Deployer</name>
<description>Overlayed deployer</description> <description>Overlayed deployer</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
</properties> </properties>
<build> <build>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-plus</artifactId> <artifactId>jetty-plus</artifactId>
<name>Jetty :: Plus</name> <name>Jetty :: Plus</name>
<description>Jetty JavaEE style services</description> <description>Jetty JavaEE style services</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.plus</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.plus</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-policy</artifactId> <artifactId>jetty-policy</artifactId>
<name>Jetty :: Policy Tool</name> <name>Jetty :: Policy Tool</name>
<packaging>jar</packaging> <packaging>jar</packaging>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<jetty.test.policy.loc>target/test-policy</jetty.test.policy.loc> <jetty.test.policy.loc>target/test-policy</jetty.test.policy.loc>
<bundle-symbolic-name>${project.groupId}.policy</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.policy</bundle-symbolic-name>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-rewrite</artifactId> <artifactId>jetty-rewrite</artifactId>
<name>Jetty :: Rewrite Handler</name> <name>Jetty :: Rewrite Handler</name>
<description>Jetty Rewrite Handler</description> <description>Jetty Rewrite Handler</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.rewrite</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.rewrite</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-security</artifactId> <artifactId>jetty-security</artifactId>
<name>Jetty :: Security</name> <name>Jetty :: Security</name>
<description>Jetty security infrastructure</description> <description>Jetty security infrastructure</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.security</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.security</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<name>Jetty :: Server Core</name> <name>Jetty :: Server Core</name>
<description>The core jetty server artifact.</description> <description>The core jetty server artifact.</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.server</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.server</bundle-symbolic-name>
</properties> </properties>

View File

@ -570,6 +570,7 @@ public class Response implements HttpServletResponse
*/ */
public void addHeader(String name, String value) public void addHeader(String name, String value)
{ {
if (_connection.isIncluding()) if (_connection.isIncluding())
{ {
if (name.startsWith(SET_INCLUDE_HEADER_PREFIX)) if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
@ -578,6 +579,12 @@ public class Response implements HttpServletResponse
return; return;
} }
if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name))
{
setContentType(value);
return;
}
_connection.getResponseFields().add(name, value); _connection.getResponseFields().add(name, value);
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
_connection._generator.setContentLength(Long.parseLong(value)); _connection._generator.setContentLength(Long.parseLong(value));

View File

@ -2191,7 +2191,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
if (dot<0) if (dot<0)
return false; return false;
String suffix=path.substring(dot); String suffix=path.substring(dot);
return resource.getAlias().toString().endsWith(suffix); return resource.toString().endsWith(suffix);
} }
} }
@ -2206,10 +2206,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
public boolean check(String path, Resource resource) public boolean check(String path, Resource resource)
{ {
int slash = path.lastIndexOf('/'); int slash = path.lastIndexOf('/');
if (slash<0) if (slash<0 || slash==path.length()-1)
return false; return false;
String suffix=path.substring(slash); String suffix=path.substring(slash);
return resource.getAlias().toString().endsWith(suffix); return resource.toString().endsWith(suffix);
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -2222,7 +2222,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
public boolean check(String path, Resource resource) public boolean check(String path, Resource resource)
{ {
int slash = path.lastIndexOf('/'); int slash = path.lastIndexOf('/');
if (slash<0) if (slash<0 || resource.exists())
return false; return false;
String suffix=path.substring(slash); String suffix=path.substring(slash);
return resource.getAlias().toString().endsWith(suffix); return resource.getAlias().toString().endsWith(suffix);

View File

@ -24,22 +24,23 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.server.AsyncContinuation; import org.eclipse.jetty.server.AsyncContinuation;
import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
/** /**
* RequestLogHandler. * RequestLogHandler.
* This handler can be used to wrap an individual context for context logging. * This handler can be used to wrap an individual context for context logging.
* *
*
* @org.apache.xbean.XBean * @org.apache.xbean.XBean
*/ */
public class RequestLogHandler extends HandlerWrapper public class RequestLogHandler extends HandlerWrapper
@ -53,7 +54,7 @@ public class RequestLogHandler extends HandlerWrapper
* @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
*/ */
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) public void handle(String target, final Request baseRequest, HttpServletRequest request, final HttpServletResponse response)
throws IOException, ServletException throws IOException, ServletException
{ {
AsyncContinuation continuation = baseRequest.getAsyncContinuation(); AsyncContinuation continuation = baseRequest.getAsyncContinuation();
@ -68,11 +69,25 @@ public class RequestLogHandler extends HandlerWrapper
} }
finally finally
{ {
if (_requestLog != null && DispatcherType.REQUEST.equals(baseRequest.getDispatcherType())) if (continuation.isAsync())
{
if (continuation.isInitial())
continuation.addContinuationListener(new ContinuationListener()
{
public void onTimeout(Continuation continuation)
{
}
public void onComplete(Continuation continuation)
{ {
_requestLog.log(baseRequest, (Response)response); _requestLog.log(baseRequest, (Response)response);
} }
});
}
else
_requestLog.log(baseRequest, (Response)response);
} }
} }
@ -139,8 +154,12 @@ public class RequestLogHandler extends HandlerWrapper
@Override @Override
protected void doStart() throws Exception protected void doStart() throws Exception
{ {
if (_requestLog==null)
{
LOG.warn("!RequestLog");
_requestLog=new NullRequestLog();
}
super.doStart(); super.doStart();
if (_requestLog!=null)
_requestLog.start(); _requestLog.start();
} }
@ -152,8 +171,19 @@ public class RequestLogHandler extends HandlerWrapper
protected void doStop() throws Exception protected void doStop() throws Exception
{ {
super.doStop(); super.doStop();
if (_requestLog!=null)
_requestLog.stop(); _requestLog.stop();
if (_requestLog instanceof NullRequestLog)
_requestLog=null;
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private static class NullRequestLog extends AbstractLifeCycle implements RequestLog
{
public void log(Request request, Response response)
{
}
} }
} }

View File

@ -170,6 +170,10 @@ public class ResponseTest
response.setContentType("foo/bar"); response.setContentType("foo/bar");
assertEquals("foo/bar", response.getContentType()); assertEquals("foo/bar", response.getContentType());
response.recycle();
response.addHeader("Content-Type","text/something");
assertEquals("text/something",response.getContentType());
} }
@Test @Test

View File

@ -9,6 +9,7 @@
<artifactId>jetty-servlet</artifactId> <artifactId>jetty-servlet</artifactId>
<name>Jetty :: Servlet Handling</name> <name>Jetty :: Servlet Handling</name>
<description>Jetty Servlet Container</description> <description>Jetty Servlet Container</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.servlet</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.servlet</bundle-symbolic-name>
</properties> </properties>

View File

@ -756,6 +756,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{ {
r.reset(true); r.reset(true);
r.setStatus(HttpServletResponse.SC_NOT_MODIFIED); r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
if (_etags)
r.getHttpFields().add(HttpHeaders.ETAG_BUFFER,content.getETag());
r.flushBuffer(); r.flushBuffer();
return false; return false;
} }
@ -769,6 +771,8 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
{ {
r.reset(true); r.reset(true);
r.setStatus(HttpServletResponse.SC_NOT_MODIFIED); r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
if (_etags)
r.getHttpFields().add(HttpHeaders.ETAG_BUFFER,content.getETag());
r.flushBuffer(); r.flushBuffer();
return false; return false;
} }

View File

@ -625,6 +625,8 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
Servlet servlet=_servlet; Servlet servlet=_servlet;
synchronized(this) synchronized(this)
{ {
if (!isStarted())
throw new UnavailableException("Servlet not initialized", -1);
if (_unavailable!=0 || !_initOnStartup) if (_unavailable!=0 || !_initOnStartup)
servlet=getServlet(); servlet=getServlet();
if (servlet==null) if (servlet==null)

View File

@ -18,8 +18,10 @@
package org.eclipse.jetty.servlet; package org.eclipse.jetty.servlet;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -55,6 +57,7 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -586,7 +589,14 @@ public class DispatcherTest
assertEquals(null, request.getPathInfo()); assertEquals(null, request.getPathInfo());
assertEquals(null, request.getPathTranslated()); assertEquals(null, request.getPathTranslated());
assertTrue(request.getQueryString().startsWith("do=end&else=%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE%3D%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0&test=1&foreign="));
UrlEncoded query = new UrlEncoded(request.getQueryString());
assertThat(query.getString("do"), is("end"));
// Russian for "selected=Temperature"
String russian = new UrlEncoded(query.getString("else")).encode();
assertThat(russian, is("%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BE=%D0%A2%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0"));
assertThat(query.getString("test"), is("1"));
assertThat(query.containsKey("foreign"), is(true));
String[] vals = request.getParameterValues("foreign"); String[] vals = request.getParameterValues("foreign");
assertTrue(vals!=null); assertTrue(vals!=null);

View File

@ -9,6 +9,7 @@
<artifactId>jetty-servlets</artifactId> <artifactId>jetty-servlets</artifactId>
<name>Jetty :: Utility Servlets and Filters</name> <name>Jetty :: Utility Servlets and Filters</name>
<description>Utility Servlets from Jetty</description> <description>Utility Servlets from Jetty</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.servlets</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.servlets</bundle-symbolic-name>
</properties> </properties>

View File

@ -22,6 +22,8 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@ -32,8 +34,11 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -222,35 +227,55 @@ public class CGI extends HttpServlet
if ((pathTranslated == null) || (pathTranslated.length() == 0)) if ((pathTranslated == null) || (pathTranslated.length() == 0))
pathTranslated = path; pathTranslated = path;
String bodyFormEncoded = null;
if ((HttpMethods.POST.equals(req.getMethod()) || HttpMethods.PUT.equals(req.getMethod())) && "application/x-www-form-urlencoded".equals(req.getContentType()))
{
MultiMap<String> parameterMap = new MultiMap<String>();
Enumeration names = req.getParameterNames();
while (names.hasMoreElements())
{
String parameterName = (String)names.nextElement();
parameterMap.addValues(parameterName, req.getParameterValues(parameterName));
}
bodyFormEncoded = UrlEncoded.encode(parameterMap, req.getCharacterEncoding(), true);
}
EnvList env = new EnvList(_env); EnvList env = new EnvList(_env);
// these ones are from "The WWW Common Gateway Interface Version 1.1" // these ones are from "The WWW Common Gateway Interface Version 1.1"
// look at : // look at :
// http://Web.Golux.Com/coar/cgi/draft-coar-cgi-v11-03-clean.html#6.1.1 // http://Web.Golux.Com/coar/cgi/draft-coar-cgi-v11-03-clean.html#6.1.1
env.set("AUTH_TYPE",req.getAuthType()); env.set("AUTH_TYPE", req.getAuthType());
env.set("CONTENT_LENGTH",Integer.toString(len)); if (bodyFormEncoded != null)
env.set("CONTENT_TYPE",req.getContentType()); {
env.set("GATEWAY_INTERFACE","CGI/1.1"); env.set("CONTENT_LENGTH", Integer.toString(bodyFormEncoded.length()));
}
else
{
env.set("CONTENT_LENGTH", Integer.toString(len));
}
env.set("CONTENT_TYPE", req.getContentType());
env.set("GATEWAY_INTERFACE", "CGI/1.1");
if ((pathInfo != null) && (pathInfo.length() > 0)) if ((pathInfo != null) && (pathInfo.length() > 0))
{ {
env.set("PATH_INFO",pathInfo); env.set("PATH_INFO", pathInfo);
} }
env.set("PATH_TRANSLATED",pathTranslated); env.set("PATH_TRANSLATED", pathTranslated);
env.set("QUERY_STRING",req.getQueryString()); env.set("QUERY_STRING", req.getQueryString());
env.set("REMOTE_ADDR",req.getRemoteAddr()); env.set("REMOTE_ADDR", req.getRemoteAddr());
env.set("REMOTE_HOST",req.getRemoteHost()); env.set("REMOTE_HOST", req.getRemoteHost());
// The identity information reported about the connection by a // The identity information reported about the connection by a
// RFC 1413 [11] request to the remote agent, if // RFC 1413 [11] request to the remote agent, if
// available. Servers MAY choose not to support this feature, or // available. Servers MAY choose not to support this feature, or
// not to request the data for efficiency reasons. // not to request the data for efficiency reasons.
// "REMOTE_IDENT" => "NYI" // "REMOTE_IDENT" => "NYI"
env.set("REMOTE_USER",req.getRemoteUser()); env.set("REMOTE_USER", req.getRemoteUser());
env.set("REQUEST_METHOD",req.getMethod()); env.set("REQUEST_METHOD", req.getMethod());
env.set("SCRIPT_NAME",scriptName); env.set("SCRIPT_NAME", scriptName);
env.set("SCRIPT_FILENAME",scriptPath); env.set("SCRIPT_FILENAME", scriptPath);
env.set("SERVER_NAME",req.getServerName()); env.set("SERVER_NAME", req.getServerName());
env.set("SERVER_PORT",Integer.toString(req.getServerPort())); env.set("SERVER_PORT", Integer.toString(req.getServerPort()));
env.set("SERVER_PROTOCOL",req.getProtocol()); env.set("SERVER_PROTOCOL", req.getProtocol());
env.set("SERVER_SOFTWARE",getServletContext().getServerInfo()); env.set("SERVER_SOFTWARE", getServletContext().getServerInfo());
Enumeration enm = req.getHeaderNames(); Enumeration enm = req.getHeaderNames();
while (enm.hasMoreElements()) while (enm.hasMoreElements())
@ -261,7 +286,7 @@ public class CGI extends HttpServlet
} }
// these extra ones were from printenv on www.dev.nomura.co.uk // these extra ones were from printenv on www.dev.nomura.co.uk
env.set("HTTPS",(req.isSecure()?"ON":"OFF")); env.set("HTTPS", (req.isSecure()?"ON":"OFF"));
// "DOCUMENT_ROOT" => root + "/docs", // "DOCUMENT_ROOT" => root + "/docs",
// "SERVER_URL" => "NYI - http://us0245", // "SERVER_URL" => "NYI - http://us0245",
// "TZ" => System.getProperty("user.timezone"), // "TZ" => System.getProperty("user.timezone"),
@ -275,31 +300,22 @@ public class CGI extends HttpServlet
if (_cmdPrefix != null) if (_cmdPrefix != null)
execCmd = _cmdPrefix + " " + execCmd; execCmd = _cmdPrefix + " " + execCmd;
Process p = (dir == null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir); LOG.debug("Environment: " + env.getExportString());
LOG.debug("Command: " + execCmd);
Process p;
if (dir == null)
p = Runtime.getRuntime().exec(execCmd, env.getEnvArray());
else
p = Runtime.getRuntime().exec(execCmd, env.getEnvArray(), dir);
// hook processes input to browser's output (async) // hook processes input to browser's output (async)
final InputStream inFromReq = req.getInputStream(); if (bodyFormEncoded != null)
final OutputStream outToCgi = p.getOutputStream(); writeProcessInput(p, bodyFormEncoded);
final int inLength = len; else if (len > 0)
writeProcessInput(p, req.getInputStream(), len);
IO.copyThread(p.getErrorStream(),System.err); IO.copyThread(p.getErrorStream(), System.err);
new Thread(new Runnable()
{
public void run()
{
try
{
if (inLength > 0)
IO.copy(inFromReq,outToCgi,inLength);
outToCgi.close();
}
catch (IOException e)
{
LOG.ignore(e);
}
}
}).start();
// hook processes output to browser's input (sync) // hook processes output to browser's input (sync)
// if browser closes stream, we should detect it and kill process... // if browser closes stream, we should detect it and kill process...
@ -376,15 +392,56 @@ public class CGI extends HttpServlet
} }
catch (Exception e) catch (Exception e)
{ {
LOG.ignore(e); LOG.debug(e);
} }
} }
os = null;
p.destroy(); p.destroy();
// LOG.debug("CGI: terminated!"); // LOG.debug("CGI: terminated!");
} }
} }
private static void writeProcessInput(final Process p, final String input)
{
new Thread(new Runnable()
{
public void run()
{
try
{
Writer outToCgi = new OutputStreamWriter(p.getOutputStream());
outToCgi.write(input);
outToCgi.close();
}
catch (IOException e)
{
LOG.debug(e);
}
}
}).start();
}
private static void writeProcessInput(final Process p, final InputStream input, final int len)
{
if (len <= 0) return;
new Thread(new Runnable()
{
public void run()
{
try
{
OutputStream outToCgi = p.getOutputStream();
IO.copy(input, outToCgi, len);
outToCgi.close();
}
catch (IOException e)
{
LOG.debug(e);
}
}
}).start();
}
/** /**
* Utility method to get a line of text from the input stream. * Utility method to get a line of text from the input stream.
* *
@ -393,7 +450,7 @@ public class CGI extends HttpServlet
* @return the line of text * @return the line of text
* @throws IOException * @throws IOException
*/ */
private String getTextLineFromStream(InputStream is) throws IOException private static String getTextLineFromStream(InputStream is) throws IOException
{ {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
int b; int b;
@ -411,16 +468,16 @@ public class CGI extends HttpServlet
*/ */
private static class EnvList private static class EnvList
{ {
private Map envMap; private Map<String, String> envMap;
EnvList() EnvList()
{ {
envMap = new HashMap(); envMap = new HashMap<String, String>();
} }
EnvList(EnvList l) EnvList(EnvList l)
{ {
envMap = new HashMap(l.envMap); envMap = new HashMap<String,String>(l.envMap);
} }
/** /**
@ -434,7 +491,19 @@ public class CGI extends HttpServlet
/** Get representation suitable for passing to exec. */ /** Get representation suitable for passing to exec. */
public String[] getEnvArray() public String[] getEnvArray()
{ {
return (String[])envMap.values().toArray(new String[envMap.size()]); return envMap.values().toArray(new String[envMap.size()]);
}
public String getExportString()
{
StringBuilder sb = new StringBuilder();
for (String variable : getEnvArray())
{
sb.append("export \"");
sb.append(variable);
sb.append("\"; ");
}
return sb.toString();
} }
@Override @Override

View File

@ -48,12 +48,15 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletRequestWrapper;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.ReadLineInputStream; import org.eclipse.jetty.util.ReadLineInputStream;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -174,13 +177,12 @@ public class MultiPartFilter implements Filter
// Read each part // Read each part
boolean lastPart=false; boolean lastPart=false;
String content_disposition=null;
String content_transfer_encoding=null;
outer:while(!lastPart && params.size()<_maxFormKeys) outer:while(!lastPart && params.size()<_maxFormKeys)
{ {
String type_content=null; String type_content=null;
String content_disposition=null;
String content_transfer_encoding=null;
while(true) while(true)
{ {
@ -283,7 +285,7 @@ public class MultiPartFilter implements Filter
if ("base64".equalsIgnoreCase(content_transfer_encoding)) if ("base64".equalsIgnoreCase(content_transfer_encoding))
{ {
in = new Base64InputStream(in); in = new Base64InputStream((ReadLineInputStream)in);
} }
else if ("quoted-printable".equalsIgnoreCase(content_transfer_encoding)) else if ("quoted-printable".equalsIgnoreCase(content_transfer_encoding))
{ {
@ -513,12 +515,11 @@ public class MultiPartFilter implements Filter
{ {
try try
{ {
String s=new String((byte[])o,_encoding); return getParameterBytesAsString(name, (byte[])o);
return s;
} }
catch(Exception e) catch(Exception e)
{ {
e.printStackTrace(); LOG.warn(e);
} }
} }
else if (o!=null) else if (o!=null)
@ -533,11 +534,11 @@ public class MultiPartFilter implements Filter
@Override @Override
public Map getParameterMap() public Map getParameterMap()
{ {
Map<String, String> cmap = new HashMap<String,String>(); Map<String, String[]> cmap = new HashMap<String,String[]>();
for ( Object key : _params.keySet() ) for ( Object key : _params.keySet() )
{ {
cmap.put((String)key,getParameter((String)key)); cmap.put((String)key,getParameterValues((String)key));
} }
return Collections.unmodifiableMap(cmap); return Collections.unmodifiableMap(cmap);
@ -571,7 +572,7 @@ public class MultiPartFilter implements Filter
{ {
try try
{ {
v[i]=new String((byte[])o,_encoding); v[i]=getParameterBytesAsString(name, (byte[])o);
} }
catch(Exception e) catch(Exception e)
{ {
@ -594,18 +595,36 @@ public class MultiPartFilter implements Filter
{ {
_encoding=enc; _encoding=enc;
} }
/* ------------------------------------------------------------------------------- */
private String getParameterBytesAsString (String name, byte[] bytes)
throws UnsupportedEncodingException
{
//check if there is a specific encoding for the parameter
Object ct = _params.get(name+CONTENT_TYPE_SUFFIX);
//use default if not
String contentType = _encoding;
if (ct != null)
{
String tmp = MimeTypes.getCharsetFromContentType(new ByteArrayBuffer((String)ct));
contentType = (tmp == null?_encoding:tmp);
}
return new String(bytes,contentType);
}
} }
private static class Base64InputStream extends InputStream private static class Base64InputStream extends InputStream
{ {
BufferedReader _in; ReadLineInputStream _in;
String _line; String _line;
byte[] _buffer; byte[] _buffer;
int _pos; int _pos;
public Base64InputStream (InputStream in) public Base64InputStream (ReadLineInputStream in)
{ {
_in = new BufferedReader(new InputStreamReader(in)); _in = in;
} }
@Override @Override
@ -614,6 +633,7 @@ public class MultiPartFilter implements Filter
if (_buffer==null || _pos>= _buffer.length) if (_buffer==null || _pos>= _buffer.length)
{ {
_line = _in.readLine(); _line = _in.readLine();
System.err.println("LINE: "+_line);
if (_line==null) if (_line==null)
return -1; return -1;
if (_line.startsWith("--")) if (_line.startsWith("--"))
@ -621,7 +641,13 @@ public class MultiPartFilter implements Filter
else if (_line.length()==0) else if (_line.length()==0)
_buffer="\r\n".getBytes(); _buffer="\r\n".getBytes();
else else
_buffer=B64Code.decode(_line); {
ByteArrayOutputStream bout = new ByteArrayOutputStream(4*_line.length()/3);
B64Code.decode(_line, bout);
bout.write(13);
bout.write(10);
_buffer = bout.toByteArray();
}
_pos=0; _pos=0;
} }

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.servlets;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.servlet.Servlet; import javax.servlet.Servlet;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
@ -30,10 +31,14 @@ import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite; import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite; import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite;
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite; import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWriteWithFlush;
import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite; import org.eclipse.jetty.servlets.gzip.TestServletStreamTypeLengthWrite;
import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite; import org.eclipse.jetty.servlets.gzip.TestServletTypeLengthStreamWrite;
import org.eclipse.jetty.servlets.gzip.TestServletTypeStreamLengthWrite; import org.eclipse.jetty.servlets.gzip.TestServletTypeStreamLengthWrite;
import org.eclipse.jetty.testing.HttpTester;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -69,12 +74,14 @@ public class GzipFilterContentLengthTest
{ TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP }, { TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
{ TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP }, { TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
{ TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP }, { TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
{ TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
{ TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP }, { TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
{ TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP }, { TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
{ TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP }, { TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
{ TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE }, { TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
{ TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE }, { TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
{ TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE }, { TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
{ TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
{ TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE }, { TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
{ TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE }, { TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
{ TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE } { TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE }
@ -131,7 +138,8 @@ public class GzipFilterContentLengthTest
try try
{ {
tester.start(); tester.start();
tester.assertIsResponseNotGzipCompressed("GET",testfile.getName(),filesize,HttpStatus.OK_200); HttpTester response = tester.assertIsResponseNotGzipCompressed("GET",testfile.getName(),filesize,HttpStatus.OK_200);
Assert.assertThat(response.getHeader("ETAG"),Matchers.startsWith("W/etag-"));
} }
finally finally
{ {
@ -139,6 +147,15 @@ public class GzipFilterContentLengthTest
} }
} }
/**
* Tests gzip compression of a small size file
*/
@Test
public void testEmpty() throws Exception
{
assertIsNotGzipCompressed("empty.txt",0);
}
/** /**
* Tests gzip compression of a small size file * Tests gzip compression of a small size file
*/ */

View File

@ -79,6 +79,7 @@ public class GzipFilterDefaultTest
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{ {
resp.setStatus(_status); resp.setStatus(_status);
resp.setHeader("ETag","W/\"204\"");
} }
} }
@ -141,12 +142,41 @@ public class GzipFilterDefaultTest
@Override @Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException,ServletException public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException,ServletException
{ {
String uri=req.getRequestURI();
if (uri.endsWith(".deferred"))
{
// System.err.println("type for "+uri.substring(0,uri.length()-9)+" is "+getServletContext().getMimeType(uri.substring(0,uri.length()-9)));
resp.setContentType(getServletContext().getMimeType(uri.substring(0,uri.length()-9)));
}
doGet(req,resp); doGet(req,resp);
} }
} }
@Test
public void testIsGzipCompressedEmpty() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
// Test content that is smaller than the buffer.
tester.prepareServerFile("empty.txt",0);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotGzipCompressed("GET","empty.txt",0,200);
}
finally
{
tester.stop();
}
}
@Test @Test
public void testIsGzipCompressedTiny() throws Exception public void testIsGzipCompressedTiny() throws Exception
{ {
@ -243,6 +273,57 @@ public class GzipFilterDefaultTest
} }
} }
@Test
public void testGzipedIfModified() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
// Test content that is smaller than the buffer.
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("GET","file.txt",System.currentTimeMillis()-4000);
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
}
finally
{
tester.stop();
}
}
@Test
public void testNotGzipedIfNotModified() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
// Test content that is smaller than the buffer.
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
holder.setInitParameter("etags","true");
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotModified("GET","file.txt",System.currentTimeMillis()+4000);
}
finally
{
tester.stop();
}
}
@Test @Test
public void testIsNotGzipCompressedWithQ() throws Exception public void testIsNotGzipCompressedWithQ() throws Exception
{ {
@ -267,7 +348,7 @@ public class GzipFilterDefaultTest
} }
@Test @Test
public void testIsNotGzipCompressed() throws Exception public void testIsNotGzipCompressedByContentType() throws Exception
{ {
GzipTester tester = new GzipTester(testingdir, compressionType); GzipTester tester = new GzipTester(testingdir, compressionType);
@ -289,6 +370,29 @@ public class GzipFilterDefaultTest
} }
} }
@Test
public void testIsNotGzipCompressedByDeferredContentType() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
tester.prepareServerFile("file.mp3.deferred",filesize);
FilterHolder holder = tester.setContentServlet(GetServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotGzipCompressed("GET","file.mp3.deferred", filesize, HttpStatus.OK_200);
Assert.assertNull(http.getHeader("Vary"));
}
finally
{
tester.stop();
}
}
@Test @Test
public void testIsNotGzipCompressedHttpStatus() throws Exception public void testIsNotGzipCompressedHttpStatus() throws Exception
{ {

View File

@ -1,263 +0,0 @@
//
// ========================================================================
// 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.servlets;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.gzip.Hex;
import org.eclipse.jetty.servlets.gzip.NoOpOutputStream;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Test the effects of Gzip filtering when in the context of HTTP/1.1 Pipelining.
*/
@RunWith(Parameterized.class)
public class GzipWithPipeliningTest
{
@Parameters
public static Collection<String[]> data()
{
// Test different Content-Encoding header combinations. So implicitly testing that gzip is preferred oder deflate
String[][] data = new String[][]
{
{ GzipFilter.GZIP },
{ GzipFilter.DEFLATE + ", " + GzipFilter.GZIP },
{ GzipFilter.GZIP + ", " + GzipFilter.DEFLATE },
{ GzipFilter.DEFLATE }
};
return Arrays.asList(data);
}
@Rule
public TestingDir testingdir = new TestingDir();
private Server server;
private URI serverUri;
private String encodingHeader;
public GzipWithPipeliningTest(String encodingHeader)
{
this.encodingHeader = encodingHeader;
}
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
DefaultServlet servlet = new DefaultServlet();
ServletHolder holder = new ServletHolder(servlet);
holder.setInitParameter("resourceBase",MavenTestingUtils.getTestResourcesDir().getAbsolutePath());
context.addServlet(holder,"/");
FilterHolder filter = context.addFilter(GzipFilter.class,"/*",0);
filter.setInitParameter("mimeTypes","text/plain");
server.setHandler(context);
// Start Server
server.start();
Connector conn = server.getConnectors()[0];
String host = conn.getHost();
if (host == null)
{
host = "localhost";
}
int port = conn.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port));
// System.out.printf("Server URI: %s%n",serverUri);
}
@After
public void stopServer() throws Exception
{
server.stop();
}
@Test
public void testGzipThenImagePipelining() throws Exception
{
testingdir.ensureEmpty();
File outputDir = testingdir.getDir();
PipelineHelper client = new PipelineHelper(serverUri, encodingHeader);
try
{
File txtFile = MavenTestingUtils.getTestResourceFile("lots-of-fantasy-names.txt");
File pngFile = MavenTestingUtils.getTestResourceFile("jetty_logo.png");
// Size of content, as it exists on disk, without gzip compression.
long rawsize = txtFile.length() + pngFile.length();
assertThat("Ensure that we have sufficient file size to trigger chunking",rawsize,greaterThan(300000L));
String respHeader;
client.connect();
// Request text that will be gzipped + chunked in the response
client.issueGET("/lots-of-fantasy-names.txt",true, false);
respHeader = client.readResponseHeader();
System.out.println("Response Header #1 --\n" + respHeader);
String expectedEncodingHeader = encodingHeader.equals(GzipFilter.DEFLATE) ? GzipFilter.DEFLATE : GzipFilter.GZIP;
assertThat("Content-Encoding should be gzipped",respHeader,containsString("Content-Encoding: " + expectedEncodingHeader + "\r\n"));
assertThat("Transfer-Encoding should be chunked",respHeader,containsString("Transfer-Encoding: chunked\r\n"));
// Raw output / gzipped, writted to disk (checked for sha1sum later)
File rawOutputFile = new File(outputDir, "response-1.gz");
FileOutputStream rawOutputStream = new FileOutputStream(rawOutputFile);
long chunkSize = client.readChunkSize();
System.out.println("Chunk Size: " + chunkSize);
// Read only 20% - intentionally a partial read.
System.out.println("Attempting to read partial content ...");
int readBytes = client.readBody(rawOutputStream,(int)(chunkSize * 0.20f));
System.out.printf("Read %,d bytes%n",readBytes);
// Issue another request
client.issueGET("/jetty_logo.png",true, false);
// Finish reading chunks
System.out.println("Finish reading remaining chunks ...");
String line;
chunkSize = chunkSize - readBytes;
while (chunkSize > 0)
{
readBytes = client.readBody(rawOutputStream,(int)chunkSize);
System.out.printf("Read %,d bytes%n",readBytes);
line = client.readLine();
assertThat("Chunk delim should be an empty line with CR+LF",line,is(""));
chunkSize = client.readChunkSize();
System.out.printf("Next Chunk: (0x%X) %,d bytes%n",chunkSize,chunkSize);
}
// Inter-pipeline delim
line = client.readLine();
assertThat("Inter-pipeline delim should be an empty line with CR+LF",line,is(""));
// Sha1tracking for 1st Request
MessageDigest digestTxt = MessageDigest.getInstance("SHA1");
DigestOutputStream digesterTxt = new DigestOutputStream(new NoOpOutputStream(),digestTxt);
// Decompress 1st request and calculate sha1sum
IO.close(rawOutputStream);
FileInputStream rawInputStream = new FileInputStream(rawOutputFile);
InputStream uncompressedStream = null;
if (GzipFilter.DEFLATE.equals(encodingHeader))
{
uncompressedStream = new InflaterInputStream(rawInputStream, new Inflater(true));
}
else
{
uncompressedStream = new GZIPInputStream(rawInputStream);
}
IO.copy(uncompressedStream, digesterTxt);
// Read 2nd request http response header
respHeader = client.readResponseHeader();
System.out.println("Response Header #2 --\n" + respHeader);
assertThat("Content-Encoding should NOT be gzipped",respHeader,not(containsString("Content-Encoding: gzip\r\n")));
assertThat("Transfer-Encoding should NOT be chunked",respHeader,not(containsString("Transfer-Encoding: chunked\r\n")));
// Sha1tracking for 2nd Request
MessageDigest digestImg = MessageDigest.getInstance("SHA1");
DigestOutputStream digesterImg = new DigestOutputStream(new NoOpOutputStream(),digestImg);
// Read 2nd request body
int contentLength = client.getContentLength(respHeader);
assertThat("Image Content Length",(long)contentLength,is(pngFile.length()));
client.readBody(digesterImg,contentLength);
// Validate checksums
IO.close(rawOutputStream);
assertChecksum("lots-of-fantasy-names.txt",digestTxt);
IO.close(digesterImg);
assertChecksum("jetty_logo.png",digestImg);
}
finally
{
client.disconnect();
}
}
private void assertChecksum(String testResourceFile, MessageDigest digest) throws IOException
{
String expectedSha1 = loadSha1sum(testResourceFile + ".sha1");
String actualSha1 = Hex.asHex(digest.digest());
assertEquals(testResourceFile + " / SHA1Sum of content",expectedSha1,actualSha1);
}
private String loadSha1sum(String testResourceSha1Sum) throws IOException
{
File sha1File = MavenTestingUtils.getTestResourceFile(testResourceSha1Sum);
String contents = IO.readToString(sha1File);
Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
Matcher mat = pat.matcher(contents);
assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
return mat.group();
}
}

View File

@ -21,21 +21,26 @@ package org.eclipse.jetty.servlets;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.Map;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.testing.HttpTester; import org.eclipse.jetty.testing.HttpTester;
import org.eclipse.jetty.testing.ServletTester; import org.eclipse.jetty.testing.ServletTester;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -758,13 +763,12 @@ public class MultipartFilterTest
@Override @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{ {
String content = (String)req.getParameterMap().get("\"strup\"Content-Type: application/octet-stream"); String content = (String)req.getParameter("\"strup\"Content-Type: application/octet-stream");
assertThat(content, containsString("How now brown cow.")); assertThat(content, containsString("How now brown cow."));
super.doPost(req, resp); super.doPost(req, resp);
} }
} }
/** /**
@ -809,6 +813,66 @@ public class MultipartFilterTest
assertTrue(response.getContent().indexOf("brown cow")>=0); assertTrue(response.getContent().indexOf("brown cow")>=0);
} }
public static class TestServletCharSet extends HttpServlet
{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
//test that the multipart content bytes were converted correctly from their charset to unicode
String content = (String)req.getParameter("ttt");
assertNotNull(content);
assertEquals("ttt\u01FCzzz",content);
assertEquals("application/octet-stream; charset=UTF-8",req.getParameter("ttt"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
//test that the parameter map retrieves values as String[]
Map map = req.getParameterMap();
Object o = map.get("ttt");
assertTrue(o.getClass().isArray());
super.doPost(req, resp);
}
}
@Test
public void testWithCharSet()
throws Exception
{
// generated and parsed test
HttpTester request = new HttpTester() {
};
HttpTester response = new HttpTester();
tester.addServlet(TestServletCharSet.class,"/test2");
// test GET
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/test2");
String boundary="XyXyXy";
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(("--" + boundary + "\r\n"+
"Content-Disposition: form-data; name=\"ttt\"\r\n"+
"Content-Type: application/octet-stream; charset=UTF-8\r\n\r\n").getBytes());
baos.write("ttt\u01FCzzz".getBytes(StringUtil.__UTF8));
baos.write(("\r\n--" + boundary + "--\r\n\r\n").getBytes());
request.setContentBytes(baos.toByteArray());
response.parse(tester.getResponses(new ByteArrayBuffer(request.generate().getBytes(StringUtil.__UTF8))).toString());
}
public static class DumpServlet extends HttpServlet public static class DumpServlet extends HttpServlet
{ {
private static final long serialVersionUID = 201012011130L; private static final long serialVersionUID = 201012011130L;

View File

@ -18,13 +18,8 @@
package org.eclipse.jetty.servlets.gzip; package org.eclipse.jetty.servlets.gzip;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.*;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -45,6 +40,8 @@ import java.util.zip.InflaterInputStream;
import javax.servlet.Servlet; import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
@ -54,6 +51,7 @@ import org.eclipse.jetty.testing.ServletTester;
import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.hamcrest.Matchers;
import org.junit.Assert; import org.junit.Assert;
public class GzipTester public class GzipTester
@ -78,9 +76,19 @@ public class GzipTester
return assertIsResponseGzipCompressed(method,filename,filename); return assertIsResponseGzipCompressed(method,filename,filename);
} }
public HttpTester assertIsResponseGzipCompressed(String method,String filename,long ifmodifiedsince) throws Exception
{
return assertIsResponseGzipCompressed(method,filename,filename,ifmodifiedsince);
}
public HttpTester assertIsResponseGzipCompressed(String method,String requestedFilename, String serverFilename) throws Exception public HttpTester assertIsResponseGzipCompressed(String method,String requestedFilename, String serverFilename) throws Exception
{ {
System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename); return assertIsResponseGzipCompressed(method,requestedFilename,serverFilename,-1);
}
public HttpTester assertIsResponseGzipCompressed(String method,String requestedFilename, String serverFilename, long ifmodifiedsince) throws Exception
{
//System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester request = new HttpTester(); HttpTester request = new HttpTester();
HttpTester response = new HttpTester(); HttpTester response = new HttpTester();
@ -88,6 +96,8 @@ public class GzipTester
request.setVersion("HTTP/1.0"); request.setVersion("HTTP/1.0");
request.setHeader("Host","tester"); request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType); request.setHeader("Accept-Encoding",compressionType);
if (ifmodifiedsince>0)
request.setHeader(HttpHeaders.IF_MODIFIED_SINCE,HttpFields.formatDate(ifmodifiedsince));
if (this.userAgent != null) if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent); request.setHeader("User-Agent", this.userAgent);
request.setURI("/context/" + requestedFilename); request.setURI("/context/" + requestedFilename);
@ -100,14 +110,28 @@ public class GzipTester
// Assert the response headers // Assert the response headers
Assert.assertThat("Response.method",response.getMethod(),nullValue()); Assert.assertThat("Response.method",response.getMethod(),nullValue());
// Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
Assert.assertThat("Response.header[Content-Length]",response.getHeader("Content-Length"),notNullValue()); // Response headers should have either a Transfer-Encoding indicating chunked OR a Content-Length
String contentLength = response.getHeader("Content-Length");
String transferEncoding = response.getHeader("Transfer-Encoding");
/* TODO need to check for the 3rd option of EOF content. To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests
boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0);
if(!chunked) {
Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue());
} else {
Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue());
}
*/
int qindex = compressionType.indexOf(";"); int qindex = compressionType.indexOf(";");
if (qindex < 0) if (qindex < 0)
Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),containsString(compressionType)); Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),containsString(compressionType));
else else
Assert.assertThat("Response.header[Content-Encoding]", response.getHeader("Content-Encoding"),containsString(compressionType.substring(0,qindex))); Assert.assertThat("Response.header[Content-Encoding]", response.getHeader("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
Assert.assertThat(response.getHeader("ETag"),Matchers.startsWith("W/"));
// Assert that the decompressed contents are what we expect. // Assert that the decompressed contents are what we expect.
File serverFile = testdir.getFile(serverFilename); File serverFile = testdir.getFile(serverFilename);
String expected = IO.readToString(serverFile); String expected = IO.readToString(serverFile);
@ -143,6 +167,41 @@ public class GzipTester
return response; return response;
} }
public HttpTester assertIsResponseNotModified(String method,String requestedFilename, long ifmodifiedsince) throws Exception
{
return assertIsResponseNotModified(method,requestedFilename,requestedFilename,ifmodifiedsince);
}
public HttpTester assertIsResponseNotModified(String method,String requestedFilename, String serverFilename, long ifmodifiedsince) throws Exception
{
//System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
request.setMethod(method);
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (ifmodifiedsince>0)
request.setHeader(HttpHeaders.IF_MODIFIED_SINCE,HttpFields.formatDate(ifmodifiedsince));
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setURI("/context/" + requestedFilename);
// Issue the request
ByteArrayBuffer reqsBuff = new ByteArrayBuffer(request.generate().getBytes());
// Collect the response(s)
ByteArrayBuffer respBuff = servletTester.getResponses(reqsBuff);
response.parse(respBuff.asArray());
Assert.assertThat(response.getStatus(),Matchers.equalTo(304));
Assert.assertThat(response.getHeader("ETag"),Matchers.startsWith("W/"));
return response;
}
/** /**
* Makes sure that the response contains an unfiltered file contents. * Makes sure that the response contains an unfiltered file contents.
* <p> * <p>
@ -160,7 +219,7 @@ public class GzipTester
*/ */
public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception
{ {
System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename); // System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester request = new HttpTester(); HttpTester request = new HttpTester();
HttpTester response = new HttpTester(); HttpTester response = new HttpTester();
@ -189,6 +248,8 @@ public class GzipTester
Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.getHeader("Content-Type"),notNullValue()); Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.getHeader("Content-Type"),notNullValue());
Assert.assertThat(prefix + ".header[Content-Type]",response.getHeader("Content-Type"),is(expectedContentType)); Assert.assertThat(prefix + ".header[Content-Type]",response.getHeader("Content-Type"),is(expectedContentType));
Assert.assertThat(response.getHeader("ETAG"),Matchers.startsWith("W/"));
ByteArrayInputStream bais = null; ByteArrayInputStream bais = null;
DigestOutputStream digester = null; DigestOutputStream digester = null;
try try
@ -313,11 +374,15 @@ public class GzipTester
int serverLength = Integer.parseInt(response.getHeader("Content-Length")); int serverLength = Integer.parseInt(response.getHeader("Content-Length"));
Assert.assertThat("Response.header[Content-Length]",serverLength,is(expectedFilesize)); Assert.assertThat("Response.header[Content-Length]",serverLength,is(expectedFilesize));
} }
if (status>=200 && status<300)
Assert.assertThat(response.getHeader("ETAG"),Matchers.startsWith("W/"));
} }
private HttpTester executeRequest(String method,String uri) throws IOException, Exception private HttpTester executeRequest(String method,String uri) throws IOException, Exception
{ {
System.err.printf("[GzipTester] requesting %s%n",uri); //System.err.printf("[GzipTester] requesting %s%n",uri);
HttpTester request = new HttpTester(); HttpTester request = new HttpTester();
HttpTester response = new HttpTester(); HttpTester response = new HttpTester();
@ -345,11 +410,11 @@ public class GzipTester
ByteArrayOutputStream out = null; ByteArrayOutputStream out = null;
try try
{ {
in = new ByteArrayInputStream(response.getContentBytes()); byte[] content=response.getContentBytes();
out = new ByteArrayOutputStream(); if (content!=null)
IO.copy(in,out); actual=new String(response.getContentBytes(),encoding);
else
actual = out.toString(encoding); actual="";
} }
finally finally
{ {
@ -464,6 +529,7 @@ public class GzipTester
servletTester.setResourceBase(testdir.getDir().getCanonicalPath()); servletTester.setResourceBase(testdir.getDir().getCanonicalPath());
ServletHolder servletHolder = servletTester.addServlet(servletClass,"/"); ServletHolder servletHolder = servletTester.addServlet(servletClass,"/");
servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath()); servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath());
servletHolder.setInitParameter("etags","true");
FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",0); FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",0);
holder.setInitParameter("vary","Accept-Encoding"); holder.setInitParameter("vary","Accept-Encoding");
return holder; return holder;

View File

@ -51,6 +51,7 @@ public class TestMinGzipSizeServlet extends TestDirContentServlet
byte[] dataBytes = loadContentFileBytes(fileName); byte[] dataBytes = loadContentFileBytes(fileName);
response.setContentLength(dataBytes.length); response.setContentLength(dataBytes.length);
response.setHeader("ETag","W/etag-"+fileName);
if (fileName.endsWith(".js")) if (fileName.endsWith(".js"))
{ {
// intentionally long-form content type to test ";" splitting in code // intentionally long-form content type to test ";" splitting in code

View File

@ -59,6 +59,7 @@ public class TestServletLengthStreamTypeWrite extends TestDirContentServlet
response.setContentType("text/plain"); response.setContentType("text/plain");
else if (fileName.endsWith("mp3")) else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
out.write(dataBytes); out.write(dataBytes);
} }

View File

@ -57,6 +57,7 @@ public class TestServletLengthTypeStreamWrite extends TestDirContentServlet
response.setContentType("text/plain"); response.setContentType("text/plain");
else if (fileName.endsWith("mp3")) else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
ServletOutputStream out = response.getOutputStream(); ServletOutputStream out = response.getOutputStream();
out.write(dataBytes); out.write(dataBytes);

View File

@ -59,6 +59,7 @@ public class TestServletStreamLengthTypeWrite extends TestDirContentServlet
response.setContentType("text/plain"); response.setContentType("text/plain");
else if (fileName.endsWith("mp3")) else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
out.write(dataBytes); out.write(dataBytes);
} }

View File

@ -0,0 +1,72 @@
//
// ========================================================================
// 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.servlets.gzip;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlets.GzipFilter;
/**
* A sample servlet to serve static content, using a order of construction that has caused problems for
* {@link GzipFilter} in the past.
*
* Using a real-world pattern of:
*
* <pre>
* 1) get stream
* 2) set content length
* 3) set content type
* 4) write and flush
* </pre>
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@SuppressWarnings("serial")
public class TestServletStreamLengthTypeWriteWithFlush extends TestDirContentServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String fileName = request.getServletPath();
byte[] dataBytes = loadContentFileBytes(fileName);
ServletOutputStream out = response.getOutputStream();
// set content-length of uncompressed content (GzipFilter should handle this)
response.setContentLength(dataBytes.length);
if (fileName.endsWith("txt"))
response.setContentType("text/plain");
else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
for ( int i = 0 ; i < dataBytes.length ; i++)
{
out.write(dataBytes[i]);
// flush using response object (not the stream itself)
response.flushBuffer();
}
}
}

View File

@ -57,6 +57,7 @@ public class TestServletStreamTypeLengthWrite extends TestDirContentServlet
response.setContentType("text/plain"); response.setContentType("text/plain");
else if (fileName.endsWith("mp3")) else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
response.setContentLength(dataBytes.length); response.setContentLength(dataBytes.length);

View File

@ -55,6 +55,7 @@ public class TestServletTypeLengthStreamWrite extends TestDirContentServlet
response.setContentType("text/plain"); response.setContentType("text/plain");
else if (fileName.endsWith("mp3")) else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
response.setContentLength(dataBytes.length); response.setContentLength(dataBytes.length);

View File

@ -55,6 +55,7 @@ public class TestServletTypeStreamLengthWrite extends TestDirContentServlet
response.setContentType("text/plain"); response.setContentType("text/plain");
else if (fileName.endsWith("mp3")) else if (fileName.endsWith("mp3"))
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
response.setHeader("ETag","W/etag-"+fileName);
ServletOutputStream out = response.getOutputStream(); ServletOutputStream out = response.getOutputStream();

View File

@ -68,6 +68,7 @@ public class TestStaticMimeTypeServlet extends TestDirContentServlet
byte[] dataBytes = loadContentFileBytes(fileName); byte[] dataBytes = loadContentFileBytes(fileName);
response.setContentLength(dataBytes.length); response.setContentLength(dataBytes.length);
response.setHeader("ETag","W/etag-"+fileName);
Buffer buf = mimeTypes.getMimeByExtension(fileName); Buffer buf = mimeTypes.getMimeByExtension(fileName);
if (buf == null) if (buf == null)

View File

@ -11,7 +11,7 @@
<artifactId>spdy-parent</artifactId> <artifactId>spdy-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Jetty :: SPDY :: Parent</name> <name>Jetty :: SPDY :: Parent</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<npn.version>1.1.5.v20130313</npn.version> <npn.version>1.1.5.v20130313</npn.version>
<npn.api.version>1.1.0.v20120525</npn.api.version> <npn.api.version>1.1.0.v20120525</npn.api.version>

View File

@ -9,7 +9,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spdy-core</artifactId> <artifactId>spdy-core</artifactId>
<name>Jetty :: SPDY :: Core</name> <name>Jetty :: SPDY :: Core</name>
<url>http://www.eclipse.org/jetty</url>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -9,7 +9,7 @@
<artifactId>spdy-jetty-http-webapp</artifactId> <artifactId>spdy-jetty-http-webapp</artifactId>
<packaging>war</packaging> <packaging>war</packaging>
<name>Jetty :: SPDY :: Jetty HTTP Web Application</name> <name>Jetty :: SPDY :: Jetty HTTP Web Application</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http</artifactId> <artifactId>spdy-jetty-http</artifactId>
<name>Jetty :: SPDY :: Jetty HTTP Layer</name> <name>Jetty :: SPDY :: Jetty HTTP Layer</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty</artifactId> <artifactId>spdy-jetty</artifactId>
<name>Jetty :: SPDY :: Jetty Binding</name> <name>Jetty :: SPDY :: Jetty Binding</name>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-start</artifactId> <artifactId>jetty-start</artifactId>
<name>Jetty :: Start</name> <name>Jetty :: Start</name>
<description>The start utility</description> <description>The start utility</description>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
<name>Jetty :: Utilities</name> <name>Jetty :: Utilities</name>
<description>Utility classes for Jetty</description> <description>Utility classes for Jetty</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.util</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.util</bundle-symbolic-name>
</properties> </properties>

View File

@ -351,10 +351,32 @@ public class B64Code
if (encoded==null) if (encoded==null)
return null; return null;
ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
decode(encoded, bout);
return bout.toByteArray();
}
/* ------------------------------------------------------------ */
/**
* Base 64 decode as described in RFC 2045.
* <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
* @param encoded String to decode.
* @param output stream for decoded bytes
* @return byte array containing the decoded form of the input.
* @throws IllegalArgumentException if the input is not a valid
* B64 encoding.
*/
static public void decode (String encoded, ByteArrayOutputStream bout)
{
if (encoded==null)
return;
if (bout == null)
throw new IllegalArgumentException("No outputstream for decoded bytes");
int ci=0; int ci=0;
byte nibbles[] = new byte[4]; byte nibbles[] = new byte[4];
int s=0; int s=0;
ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
while (ci<encoded.length()) while (ci<encoded.length())
{ {
@ -390,9 +412,10 @@ public class B64Code
} }
return bout.toByteArray(); return;
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public static void encode(int value,Appendable buf) throws IOException public static void encode(int value,Appendable buf) throws IOException
{ {

View File

@ -8,6 +8,7 @@
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>
<name>Jetty :: Webapp Application Support</name> <name>Jetty :: Webapp Application Support</name>
<description>Jetty web application support</description> <description>Jetty web application support</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name>
</properties> </properties>

View File

@ -750,6 +750,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
String welcome = indexNode.toString(false, true); String welcome = indexNode.toString(false, true);
//Servlet Spec 3.0 p. 74 welcome files are additive //Servlet Spec 3.0 p. 74 welcome files are additive
if (welcome != null && welcome.trim().length() > 0)
context.setWelcomeFiles((String[])LazyList.addToArray(context.getWelcomeFiles(),welcome,String.class)); context.setWelcomeFiles((String[])LazyList.addToArray(context.getWelcomeFiles(),welcome,String.class));
} }
} }

View File

@ -9,7 +9,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>jetty-websocket</artifactId> <artifactId>jetty-websocket</artifactId>
<name>Jetty :: Websocket</name> <name>Jetty :: Websocket</name>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.websocket</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.websocket</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,6 +8,7 @@
<artifactId>jetty-xml</artifactId> <artifactId>jetty-xml</artifactId>
<name>Jetty :: XML utilities</name> <name>Jetty :: XML utilities</name>
<description>The jetty xml utilities.</description> <description>The jetty xml utilities.</description>
<url>http://www.eclipse.org/jetty</url>
<properties> <properties>
<bundle-symbolic-name>${project.groupId}.xml</bundle-symbolic-name> <bundle-symbolic-name>${project.groupId}.xml</bundle-symbolic-name>
</properties> </properties>

View File

@ -8,11 +8,11 @@
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>7.6.12-SNAPSHOT</version> <version>7.6.12-SNAPSHOT</version>
<name>Jetty :: Project</name> <name>Jetty :: Project</name>
<url>${jetty.url}</url> <url>http://www.eclipse.org/jetty</url>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.url>http://www.eclipse.org/jetty</jetty.url> <jetty.url>http://www.eclipse.org/jetty</jetty.url>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<orbit-servlet-api-version>2.5.0.v201103041518</orbit-servlet-api-version> <orbit-servlet-api-version>2.5.0.v201103041518</orbit-servlet-api-version>
<build-support-version>1.1</build-support-version> <build-support-version>1.1</build-support-version>
<slf4j-version>1.6.1</slf4j-version> <slf4j-version>1.6.1</slf4j-version>

View File

@ -9,6 +9,7 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Test :: Continuation - (Jetty 6)</name> <name>Test :: Continuation - (Jetty 6)</name>
<description>Asynchronous API</description> <description>Asynchronous API</description>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -9,6 +9,7 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Test :: Continuation</name> <name>Test :: Continuation</name>
<description>Asynchronous API</description> <description>Asynchronous API</description>
<url>http://www.eclipse.org/jetty</url>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -20,16 +20,25 @@ package org.eclipse.jetty.continuation;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.eclipse.jetty.continuation.test.ContinuationBase; import org.eclipse.jetty.continuation.test.ContinuationBase;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
@ -39,14 +48,22 @@ public class ContinuationTest extends ContinuationBase
protected ServletHandler _servletHandler; protected ServletHandler _servletHandler;
protected SelectChannelConnector _connector; protected SelectChannelConnector _connector;
FilterHolder _filter; FilterHolder _filter;
protected List<String> _log = new ArrayList<String>();
@Override @Override
protected void setUp() throws Exception protected void setUp() throws Exception
{ {
_connector = new SelectChannelConnector(); _connector = new SelectChannelConnector();
_server.setConnectors(new Connector[]{ _connector }); _server.setConnectors(new Connector[]{ _connector });
_log.clear();
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(new Log());
_server.setHandler(requestLogHandler);
ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS); ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
_server.setHandler(servletContext); requestLogHandler.setHandler(servletContext);
_servletHandler=servletContext.getServletHandler(); _servletHandler=servletContext.getServletHandler();
ServletHolder holder=new ServletHolder(_servlet); ServletHolder holder=new ServletHolder(_servlet);
_servletHandler.addServletWithMapping(holder,"/"); _servletHandler.addServletWithMapping(holder,"/");
@ -59,6 +76,9 @@ public class ContinuationTest extends ContinuationBase
@Override @Override
protected void tearDown() throws Exception protected void tearDown() throws Exception
{ {
Assert.assertEquals(1,_log.size());
Assert.assertTrue(_log.get(0).startsWith("200 "));
Assert.assertTrue(_log.get(0).endsWith(" /"));
_server.stop(); _server.stop();
} }
@ -153,4 +173,12 @@ public class ContinuationTest extends ContinuationBase
return IO.toString(in); return IO.toString(in);
} }
class Log extends AbstractLifeCycle implements RequestLog
{
public void log(Request request, Response response)
{
_log.add(response.getStatus()+" "+response.getContentCount()+" "+request.getRequestURI());
}
}
} }

View File

@ -8,6 +8,7 @@
</parent> </parent>
<artifactId>test-jetty-nested</artifactId> <artifactId>test-jetty-nested</artifactId>
<name>Jetty :: Nested Test</name> <name>Jetty :: Nested Test</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>war</packaging> <packaging>war</packaging>
<dependencies> <dependencies>
<dependency> <dependency>

View File

@ -8,6 +8,7 @@
<artifactId>test-jetty-servlet</artifactId> <artifactId>test-jetty-servlet</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Test :: Jetty Servlet Tester</name> <name>Test :: Jetty Servlet Tester</name>
<url>http://www.eclipse.org/jetty</url>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -532,6 +532,15 @@ public class HttpTester
} }
} }
/* ------------------------------------------------------------ */
public void setContentBytes(byte[] bytes)
{
_parsedContent = null;
_genContent = bytes;
setLongHeader(HttpHeaders.CONTENT_LENGTH, bytes.length);
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private class PH extends HttpParser.EventHandler private class PH extends HttpParser.EventHandler
{ {
@ -599,4 +608,12 @@ public class HttpTester
} }
@Override
public String toString()
{
if (_method!=null)
return super.toString()+" "+_method+" "+_uri+" "+_version+"\n"+_fields.toString();
return super.toString()+" HTTP/1.1 "+_status+" "+_reason+"\n"+_fields.toString();
}
} }

View File

@ -7,6 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-webapp</artifactId> <artifactId>test-jetty-webapp</artifactId>
<name>Test :: Jetty Test Webapp</name> <name>Test :: Jetty Test Webapp</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>war</packaging> <packaging>war</packaging>
<build> <build>
<plugins> <plugins>

Some files were not shown because too many files have changed in this diff Show More