Merge branch 'master' into release-9

This commit is contained in:
Jesse McConnell 2014-02-10 10:46:02 -06:00
commit 206b2ebf35
194 changed files with 3078 additions and 6567 deletions

View File

@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -25,7 +25,7 @@
<configuration>
<excludes>**/MANIFEST.MF,javax/**</excludes>
<excludeArtifactIds>javax</excludeArtifactIds>
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm</excludeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
@ -43,7 +43,7 @@
<excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes>
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
<excludeArtifactIds>javax</excludeArtifactIds>
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm</excludeGroupIds>
<outputDirectory>${project.build.directory}/sources</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>

0
jetty-ant/pom.xml Executable file → Normal file
View File

0
jetty-ant/src/main/resources/tasks.properties Executable file → Normal file
View File

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
@ -179,9 +180,12 @@ public class HttpProxy extends ProxyConfiguration.Proxy
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
org.eclipse.jetty.io.Connection oldConnection = endPoint.getConnection();
HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
Helper.replaceConnection(oldConnection, newConnection);
// Avoid setting fill interest in the old Connection,
// without closing the underlying EndPoint.
oldConnection.softClose();
LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
}
catch (Throwable x)

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client.http;
import java.util.Enumeration;
import org.eclipse.jetty.client.HttpChannel;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.api.Result;
@ -79,28 +77,13 @@ public class HttpChannelOverHTTP extends HttpChannel
public void exchangeTerminated(Result result)
{
super.exchangeTerminated(result);
if (result.isSucceeded())
{
HttpFields responseHeaders = result.getResponse().getHeaders();
Enumeration<String> values = responseHeaders.getValues(HttpHeader.CONNECTION.asString(), ",");
if (values != null)
{
while (values.hasMoreElements())
{
if (HttpHeaderValue.CLOSE.asString().equalsIgnoreCase(values.nextElement()))
{
connection.close();
return;
}
}
}
connection.release();
}
else
{
boolean close = result.isFailed();
HttpFields responseHeaders = result.getResponse().getHeaders();
close |= responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
if (close)
connection.close();
}
else
connection.release();
}
@Override

View File

@ -76,7 +76,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
fillInterested();
}
protected boolean isClosed()
public boolean isClosed()
{
return closed.get();
}
@ -120,7 +120,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
@Override
public void close()
{
if (closed.compareAndSet(false, true))
if (softClose())
{
getHttpDestination().close(this);
getEndPoint().shutdownOutput();
@ -130,6 +130,11 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
}
}
public boolean softClose()
{
return closed.compareAndSet(false, true);
}
@Override
public String toString()
{

View File

@ -48,6 +48,7 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
@ -58,12 +59,16 @@ import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
@ -1073,4 +1078,86 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testHTTP10WithKeepAliveAndContentLength() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// Send the headers at this point, then write the content
byte[] content = "TEST".getBytes("UTF-8");
response.setContentLength(content.length);
response.flushBuffer();
response.getOutputStream().write(content);
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.version(HttpVersion.HTTP_1_0)
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()));
}
@Test
public void testHTTP10WithKeepAliveAndNoContentLength() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// Send the headers at this point, then write the content
response.flushBuffer();
response.getOutputStream().print("TEST");
}
});
FuturePromise<Connection> promise = new FuturePromise<>();
Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort());
destination.newConnection(promise);
try (Connection connection = promise.get(5, TimeUnit.SECONDS))
{
long timeout = 5000;
Request request = client.newRequest(destination.getHost(), destination.getPort())
.scheme(destination.getScheme())
.version(HttpVersion.HTTP_1_0)
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
.timeout(timeout, TimeUnit.MILLISECONDS);
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);
ContentResponse response = listener.get(2 * timeout, TimeUnit.MILLISECONDS);
Assert.assertEquals(200, response.getStatus());
// The parser notifies end-of-content and therefore the CompleteListener
// before closing the connection, so we need to wait before checking
// that the connection is closed to avoid races.
Thread.sleep(1000);
Assert.assertTrue(((HttpConnectionOverHTTP)connection).isClosed());
}
}
@Test
public void testHTTP10WithKeepAliveAndNoContent() throws Exception
{
start(new EmptyServerHandler());
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.version(HttpVersion.HTTP_1_0)
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()));
}
}

View File

@ -427,7 +427,7 @@
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
<includeArtifactIds>javax.activation,javax.mail.glassfish</includeArtifactIds>
<includeArtifactIds>javax.mail.glassfish</includeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jndi</outputDirectory>
</configuration>
@ -440,11 +440,38 @@
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain</includeGroupIds>
<includeArtifactIds>javax.servlet.jsp.jstl,org.apache.taglibs.standard.glassfish,org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el</includeArtifactIds>
<includeArtifactIds>org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el</includeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-jstl-api</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
<includeArtifactIds>javax.servlet.jsp.jstl</includeArtifactIds>
<prependGroupId>true</prependGroupId>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-jstl-impl</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.glassfish.web</includeGroupIds>
<includeArtifactIds>javax.servlet.jsp.jstl</includeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-jaspi-deps</id>
<phase>generate-resources</phase>
@ -551,29 +578,27 @@
</build>
<dependencies>
<!-- Orbit Deps -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.activation</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.mail.glassfish</artifactId>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.security.auth.message</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>org.apache.taglibs.standard.glassfish</artifactId>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp.jstl</artifactId>
</dependency>
<dependency>

0
jetty-distribution/src/main/resources/bin/jetty.sh Executable file → Normal file
View File

View File

@ -105,7 +105,6 @@ public class HttpGenerator
_persistent = null;
_contentPrepared = 0;
_needCRLF = false;
_noContent=false;
}
/* ------------------------------------------------------------ */
@ -744,13 +743,14 @@ public class HttpGenerator
}
else
{
// No idea, so we must assume that a body is coming
_endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
{
_endOfContent=EndOfContent.NO_CONTENT;
_noContent=true;
}
// No idea, so we must assume that a body is coming.
_endOfContent = EndOfContent.CHUNKED_CONTENT;
// HTTP 1.0 does not understand chunked content, so we must use EOF content.
// For a request with HTTP 1.0 & Connection: keep-alive
// we *must* close the connection, otherwise the client
// has no way to detect the end of the content.
if (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal())
_endOfContent = EndOfContent.EOF_CONTENT;
}
break;
@ -825,7 +825,7 @@ public class HttpGenerator
header.put(CONNECTION_KEEP_ALIVE);
else
{
header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_CLOSE.length-2);
header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2);
header.put((byte)',');
header.put(StringUtil.getBytes(connection.toString()));
header.put(CRLF);
@ -833,7 +833,7 @@ public class HttpGenerator
}
else if (connection!=null)
{
header.put(CONNECTION_);
header.put(HttpHeader.CONNECTION.getBytesColonSpace());
header.put(StringUtil.getBytes(connection.toString()));
header.put(CRLF);
}
@ -872,7 +872,6 @@ public class HttpGenerator
private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: ");
private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
private static final byte[] CRLF = StringUtil.getBytes("\015\012");
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");

View File

@ -0,0 +1,368 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.http;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import org.eclipse.jetty.util.BufferUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class HttpGeneratorServerHTTPTest
{
@Parameter(value = 0)
public Run run;
private String _content;
private String _reason;
@Test
public void testHTTP() throws Exception
{
Handler handler = new Handler();
HttpGenerator gen = new HttpGenerator();
String t = run.toString();
run.result.getHttpFields().clear();
String response = run.result.build(run.httpVersion, gen, "OK\r\nTest", run.connection.val, null, run.chunks);
if (run.httpVersion == 9)
{
assertFalse(t, gen.isPersistent());
if (run.result._body != null)
assertEquals(t, run.result._body, response);
return;
}
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(run.result._head);
parser.parseNext(BufferUtil.toBuffer(response));
if (run.result._body != null)
assertEquals(t, run.result._body, this._content);
if (run.httpVersion == 10)
assertTrue(t, gen.isPersistent() || run.result._contentLength >= 0 || EnumSet.of(ConnectionType.CLOSE, ConnectionType.KEEP_ALIVE, ConnectionType.NONE).contains(run.connection));
else
assertTrue(t, gen.isPersistent() || EnumSet.of(ConnectionType.CLOSE, ConnectionType.TE_CLOSE).contains(run.connection));
if (run.httpVersion > 9)
assertEquals("OK??Test", _reason);
if (_content == null)
assertTrue(t, run.result._body == null);
else
assertThat(t, run.result._contentLength, either(equalTo(_content.length())).or(equalTo(-1)));
}
private static class Result
{
private HttpFields _fields = new HttpFields();
private final String _body;
private final int _code;
private String _connection;
private int _contentLength;
private String _contentType;
private final boolean _head;
private String _other;
private String _te;
private Result(int code, String contentType, int contentLength, String content, boolean head)
{
_code = code;
_contentType = contentType;
_contentLength = contentLength;
_other = "value";
_body = content;
_head = head;
}
private String build(int version, HttpGenerator gen, String reason, String connection, String te, int nchunks) throws Exception
{
String response = "";
_connection = connection;
_te = te;
if (_contentType != null)
_fields.put("Content-Type", _contentType);
if (_contentLength >= 0)
_fields.put("Content-Length", "" + _contentLength);
if (_connection != null)
_fields.put("Connection", _connection);
if (_te != null)
_fields.put("Transfer-Encoding", _te);
if (_other != null)
_fields.put("Other", _other);
ByteBuffer source = _body == null ? null : BufferUtil.toBuffer(_body);
ByteBuffer[] chunks = new ByteBuffer[nchunks];
ByteBuffer content = null;
int c = 0;
if (source != null)
{
for (int i = 0; i < nchunks; i++)
{
chunks[i] = source.duplicate();
chunks[i].position(i * (source.capacity() / nchunks));
if (i > 0)
chunks[i - 1].limit(chunks[i].position());
}
content = chunks[c++];
}
ByteBuffer header = null;
ByteBuffer chunk = null;
HttpGenerator.ResponseInfo info = null;
loop:
while (true)
{
// if we have unwritten content
if (source != null && content != null && content.remaining() == 0 && c < nchunks)
content = chunks[c++];
// Generate
boolean last = !BufferUtil.hasContent(content);
HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
switch (result)
{
case NEED_INFO:
info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head);
continue;
case NEED_HEADER:
header = BufferUtil.allocate(2048);
continue;
case NEED_CHUNK:
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case FLUSH:
if (BufferUtil.hasContent(header))
{
response += BufferUtil.toString(header);
header.position(header.limit());
}
if (BufferUtil.hasContent(chunk))
{
response += BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(content))
{
response += BufferUtil.toString(content);
content.position(content.limit());
}
break;
case CONTINUE:
continue;
case SHUTDOWN_OUT:
break;
case DONE:
break loop;
}
}
return response;
}
@Override
public String toString()
{
return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null ? "null" : "content") + "]";
}
public HttpFields getHttpFields()
{
return _fields;
}
}
private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
{
@Override
public boolean content(ByteBuffer ref)
{
if (_content == null)
_content = "";
_content += BufferUtil.toString(ref);
ref.position(ref.limit());
return false;
}
@Override
public void earlyEOF()
{
}
@Override
public boolean headerComplete()
{
_content = null;
return false;
}
@Override
public boolean messageComplete()
{
return true;
}
@Override
public boolean parsedHeader(HttpField field)
{
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
{
_reason = reason;
return false;
}
@Override
public void badMessage(int status, String reason)
{
throw new IllegalStateException(reason);
}
@Override
public int getHeaderCacheSize()
{
return 256;
}
}
public final static String CONTENT = "The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";
private static class Run
{
public static Run[] as(Result result, int ver, int chunks, ConnectionType connection)
{
Run run = new Run();
run.result = result;
run.httpVersion = ver;
run.chunks = chunks;
run.connection = connection;
return new Run[]{run};
}
private Result result;
private ConnectionType connection;
private int httpVersion;
private int chunks;
@Override
public String toString()
{
return String.format("result=%s,version=%d,chunks=%d,connection=%s", result, httpVersion, chunks, connection.name());
}
}
private enum ConnectionType
{
NONE(null, 9, 10, 11),
KEEP_ALIVE("keep-alive", 9, 10, 11),
CLOSE("close", 9, 10, 11),
TE_CLOSE("TE, close", 11);
private String val;
private int[] supportedHttpVersions;
private ConnectionType(String val, int... supportedHttpVersions)
{
this.val = val;
this.supportedHttpVersions = supportedHttpVersions;
}
public boolean isSupportedByHttp(int version)
{
for (int supported : supportedHttpVersions)
{
if (supported == version)
{
return true;
}
}
return false;
}
}
@Parameters(name = "{0}")
public static Collection<Run[]> data()
{
Result[] results = {
new Result(200, null, -1, null, false),
new Result(200, null, -1, CONTENT, false),
new Result(200, null, CONTENT.length(), null, true),
new Result(200, null, CONTENT.length(), CONTENT, false),
new Result(200, "text/html", -1, null, true),
new Result(200, "text/html", -1, CONTENT, false),
new Result(200, "text/html", CONTENT.length(), null, true),
new Result(200, "text/html", CONTENT.length(), CONTENT, false)
};
List<Run[]> data = new ArrayList<>();
// For each test result
for (Result result : results)
{
// Loop over HTTP versions
for (int v = 9; v <= 11; v++)
{
// Loop over chunks
for (int chunks = 1; chunks <= 6; chunks++)
{
// Loop over Connection values
for (ConnectionType connection : ConnectionType.values())
{
if (connection.isSupportedByHttp(v))
{
data.add(Run.as(result, v, chunks, connection));
}
}
}
}
}
return data;
}
}

View File

@ -18,310 +18,33 @@
package org.eclipse.jetty.http;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.util.BufferUtil;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class HttpGeneratorServerTest
{
private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
{
@Override
public boolean content(ByteBuffer ref)
{
if (_content == null)
_content = "";
_content += BufferUtil.toString(ref);
ref.position(ref.limit());
return false;
}
@Override
public void earlyEOF()
{
}
@Override
public boolean headerComplete()
{
_content = null;
return false;
}
@Override
public boolean messageComplete()
{
return true;
}
@Override
public boolean parsedHeader(HttpField field)
{
_hdr.add(field.getName());
_val.add(field.getValue());
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
{
_version = version;
_status = status;
_reason = reason;
return false;
}
@Override
public void badMessage(int status, String reason)
{
throw new IllegalStateException(reason);
}
@Override
public int getHeaderCacheSize()
{
return 256;
}
}
private static class TR
{
private HttpFields _fields = new HttpFields();
private final String _body;
private final int _code;
String _connection;
int _contentLength;
String _contentType;
private final boolean _head;
String _other;
String _te;
private TR(int code, String contentType, int contentLength, String content, boolean head)
{
_code = code;
_contentType = contentType;
_contentLength = contentLength;
_other = "value";
_body = content;
_head = head;
}
private String build(int version, HttpGenerator gen, String reason, String connection, String te, int nchunks) throws Exception
{
String response = "";
_connection = connection;
_te = te;
if (_contentType != null)
_fields.put("Content-Type", _contentType);
if (_contentLength >= 0)
_fields.put("Content-Length", "" + _contentLength);
if (_connection != null)
_fields.put("Connection", _connection);
if (_te != null)
_fields.put("Transfer-Encoding", _te);
if (_other != null)
_fields.put("Other", _other);
ByteBuffer source = _body == null ? null : BufferUtil.toBuffer(_body);
ByteBuffer[] chunks = new ByteBuffer[nchunks];
ByteBuffer content = null;
int c = 0;
if (source != null)
{
for (int i = 0; i < nchunks; i++)
{
chunks[i] = source.duplicate();
chunks[i].position(i * (source.capacity() / nchunks));
if (i > 0)
chunks[i - 1].limit(chunks[i].position());
}
content = chunks[c++];
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
ByteBuffer header = null;
ByteBuffer chunk = null;
HttpGenerator.ResponseInfo info = null;
loop:
while (true)
{
// if we have unwritten content
if (source != null && content != null && content.remaining() == 0 && c < nchunks)
{
content = chunks[c++];
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
// Generate
boolean last = !BufferUtil.hasContent(content);
HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
switch (result)
{
case NEED_INFO:
info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head);
continue;
case NEED_HEADER:
header = BufferUtil.allocate(2048);
continue;
case NEED_CHUNK:
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case FLUSH:
if (BufferUtil.hasContent(header))
{
response += BufferUtil.toString(header);
header.position(header.limit());
}
if (BufferUtil.hasContent(chunk))
{
response += BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(content))
{
response += BufferUtil.toString(content);
content.position(content.limit());
}
break;
case CONTINUE:
continue;
case SHUTDOWN_OUT:
break;
case DONE:
break loop;
}
}
return response;
}
@Override
public String toString()
{
return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null ? "null" : "content") + "]";
}
public HttpFields getHttpFields()
{
return _fields;
}
}
public final static String[] connections = {null, "keep-alive", "close", "TE, close"};
public final static String CONTENT = "The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";
private final List<String> _hdr = new ArrayList<>();
private final List<String> _val = new ArrayList<>();
private String _content;
private String _reason;
private int _status;
private HttpVersion _version;
private final TR[] tr =
{
/* 0 */ new TR(200, null, -1, null, false),
/* 1 */ new TR(200, null, -1, CONTENT, false),
/* 2 */ new TR(200, null, CONTENT.length(), null, true),
/* 3 */ new TR(200, null, CONTENT.length(), CONTENT, false),
/* 4 */ new TR(200, "text/html", -1, null, true),
/* 5 */ new TR(200, "text/html", -1, CONTENT, false),
/* 6 */ new TR(200, "text/html", CONTENT.length(), null, true),
/* 7 */ new TR(200, "text/html", CONTENT.length(), CONTENT, false),
};
@Test
public void testHTTP() throws Exception
{
Handler handler = new Handler();
// Loop over HTTP versions
for (int v = 9; v <= 11; v++)
{
// For each test result
for (int r = 0; r < tr.length; r++)
{
HttpGenerator gen = new HttpGenerator();
// Loop over chunks
for (int chunks = 1; chunks <= 6; chunks++)
{
// Loop over Connection values
for (int c = 0; c < (v == 11 ? connections.length : (connections.length - 1)); c++)
{
String t = "v=" + v + ",chunks=" + chunks + ",connection=" + connections[c] + ",tr=" + r + "=" + tr[r];
gen.reset();
tr[r].getHttpFields().clear();
String response = tr[r].build(v, gen, "OK\r\nTest", connections[c], null, chunks);
if (v == 9)
{
assertFalse(t, gen.isPersistent());
if (tr[r]._body != null)
assertEquals(t, tr[r]._body, response);
continue;
}
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(tr[r]._head);
parser.parseNext(BufferUtil.toBuffer(response));
if (tr[r]._body != null)
assertEquals(t, tr[r]._body, this._content);
if (v == 10)
assertTrue(t, gen.isPersistent() || tr[r]._contentLength >= 0 || c == 2 || c == 0);
else
assertTrue(t, gen.isPersistent() || c == 2 || c == 3);
if (v > 9)
assertEquals("OK??Test", _reason);
if (_content == null)
assertTrue(t, tr[r]._body == null);
else
assertThat(t, tr[r]._contentLength, either(equalTo(_content.length())).or(equalTo(-1)));
}
}
}
}
}
@Test
public void testSendServerXPoweredBy() throws Exception
{
ByteBuffer header = BufferUtil.allocate(8096);
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
HttpFields fields = new HttpFields();
fields.add(HttpHeader.SERVER,"SomeServer");
fields.add(HttpHeader.X_POWERED_BY,"SomePower");
fields.add(HttpHeader.SERVER, "SomeServer");
fields.add(HttpHeader.X_POWERED_BY, "SomePower");
ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false);
String head;
HttpGenerator gen = new HttpGenerator(true,true);
HttpGenerator gen = new HttpGenerator(true, true);
gen.generateResponse(info, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
@ -338,8 +61,8 @@ public class HttpGeneratorServerTest
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
assertThat(head, containsString("X-Powered-By: SomePower"));
gen.reset();
gen = new HttpGenerator(false,false);
gen = new HttpGenerator(false, false);
gen.generateResponse(info, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
@ -355,7 +78,7 @@ public class HttpGeneratorServerTest
assertThat(head, containsString("Server: SomeServer"));
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
assertThat(head, containsString("X-Powered-By: SomePower"));
gen.reset();
gen.reset();
}
@Test
@ -365,8 +88,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result = gen.generateResponse(null, null, null, null, true);
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -399,8 +121,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result = gen.generateResponse(null, null, null, null, true);
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -454,7 +175,7 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null,null,chunk, content1, false);
result = gen.generateResponse(null, null, chunk, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(chunk);
@ -462,17 +183,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null,null,chunk, null, true);
result = gen.generateResponse(null, null, chunk, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null,null,chunk, null, true);
result = gen.generateResponse(null, null, chunk, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
result = gen.generateResponse(null,null,chunk, null, true);
result = gen.generateResponse(null, null, chunk, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -495,9 +216,7 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result = gen.generateResponse(null, null, null, content0, false);
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -540,15 +259,12 @@ public class HttpGeneratorServerTest
@Test
public void test100ThenResponseWithContent() throws Exception
{
ByteBuffer header = BufferUtil.allocate(4096);
ByteBuffer content0 = BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
HttpGenerator.Result result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -563,7 +279,6 @@ public class HttpGeneratorServerTest
assertThat(out, containsString("HTTP/1.1 100 Continue"));
result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -603,4 +318,22 @@ public class HttpGeneratorServerTest
assertThat(out, containsString("Content-Length: 59"));
assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
}
@Test
public void testConnectionKeepAliveWithAdditionalCustomValue() throws Exception
{
HttpGenerator generator = new HttpGenerator();
HttpFields fields = new HttpFields();
fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
String customValue = "test";
fields.add(HttpHeader.CONNECTION, customValue);
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_0, fields, -1, 200, "OK", false);
ByteBuffer header = BufferUtil.allocate(4096);
HttpGenerator.Result result = generator.generateResponse(info, header, null, null, true);
Assert.assertSame(HttpGenerator.Result.FLUSH, result);
String headers = BufferUtil.toString(header);
Assert.assertTrue(headers.contains(HttpHeaderValue.KEEP_ALIVE.asString()));
Assert.assertTrue(headers.contains(customValue));
}
}

View File

@ -37,6 +37,7 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
LeakTrackingByteBufferPool.this.leaked(leakInfo);
}
};
private final ByteBufferPool delegate;
public LeakTrackingByteBufferPool(ByteBufferPool delegate)
@ -51,20 +52,22 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
{
ByteBuffer buffer = delegate.acquire(size, direct);
if (!leakDetector.acquired(buffer))
LOG.info("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
LOG.warn("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
return buffer;
}
@Override
public void release(ByteBuffer buffer)
{
if (buffer == null)
return;
if (!leakDetector.released(buffer))
LOG.info("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer));
LOG.warn("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer));
delegate.release(buffer);
}
protected void leaked(LeakDetector.LeakInfo leakInfo)
protected void leaked(LeakDetector<ByteBuffer>.LeakInfo leakInfo)
{
LOG.info("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
LOG.warn("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
}
}

View File

@ -62,8 +62,8 @@
</dependency>
<!-- JSTL Impl -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>org.apache.taglibs.standard.glassfish</artifactId>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp.jstl</artifactId>
</dependency>
<!-- EL Api -->

View File

@ -125,11 +125,11 @@
<artifactId>jetty-jsp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<!-- dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.activation</artifactId>
<scope>compile</scope>
</dependency>
</dependency -->
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>

View File

@ -14,8 +14,8 @@
<!-- uncomment the following lines, changing log interval (in milliseconds) -->
<!-- and log threshold (in percent) as desired. -->
<!--
<Set name="logInterval">10000</Arg>
<Set name="logThreshold">1</Arg>
<Set name="logInterval">10000</Set>
<Set name="logThreshold">1</Set>
-->
<!-- To enable detail dump of the server whenever a thread is detected as spinning, -->

View File

@ -7,7 +7,7 @@ server
client
[lib]
lib/jetty-monitor-${jetty.version}.jar
lib/monitor/jetty-monitor-${jetty.version}.jar
[xml]
etc/jetty-monitor.xml
etc/jetty-monitor.xml

View File

@ -100,10 +100,10 @@
javax.servlet.jsp;version="[2.3,2.4)",
javax.servlet.jsp.el;version="[2.3,2.4)",
javax.servlet.jsp.tagext;version="[2.3,2.4)",
javax.servlet.jsp.jstl.core;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.fmt;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.sql;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.tlv;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional,
javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
org.apache.jasper;version="[2.3.2,2.4)";resolution:=optional,
org.apache.jasper.compiler;version="[2.3.2,2.4)";resolution:=optional,
org.apache.jasper.compiler.tagplugin;version="[2.3.2,2.4)";resolution:=optional,
@ -114,29 +114,29 @@
org.apache.jasper.util;version="[2.3.2,2.4)";resolution:=optional,
org.apache.jasper.xmlparser;version="[2.3.2,2.4)";resolution:=optional,
org.glassfish.jsp.api;version="[2.3.2,2.4)";resolution:=optional,
org.apache.taglibs.standard;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.extra.spath;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.functions;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.lang.jstl;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.parser;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.test;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.lang.support;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.resources;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.common.core;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.common.fmt;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.common.sql;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.common.xml;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.el.core;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.el.fmt;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.el.sql;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.el.xml;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.rt.core;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.rt.fmt;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.rt.sql;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tag.rt.xml;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tei;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tlv;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard;version="1.2";resolution:=optional,
org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional,
org.apache.taglibs.standard.resources;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
org.osgi.*,
org.xml.*;resolution:=optional,
org.xml.sax.*;resolution:=optional,
@ -144,7 +144,7 @@
org.w3c.dom;resolution:=optional,
org.w3c.dom.ls;resolution:=optional,
javax.xml.parser;resolution:=optional
</Import-Package>
</Import-Package>
<_nouses>true</_nouses>
<DynamicImport-Package>org.apache.jasper.*;version="2.3"</DynamicImport-Package>
</instructions>

View File

@ -25,6 +25,8 @@ import org.eclipse.jetty.annotations.AnnotationParser.Handler;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
@ -37,12 +39,14 @@ import org.osgi.framework.Constants;
*/
public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration
{
private static final Logger LOG = Log.getLogger(org.eclipse.jetty.annotations.AnnotationConfiguration.class);
public class BundleParserTask extends ParserTask
{
public BundleParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver)
{
super(parser, handlers, resource, resolver);
super(parser, handlers, resource, resolver);
}
public Void call() throws Exception
@ -51,7 +55,11 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
{
org.eclipse.jetty.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.osgi.annotations.AnnotationParser)_parser;
Bundle bundle = osgiAnnotationParser.getBundle(_resource);
if (_stat != null)
_stat.start();
osgiAnnotationParser.parse(_handlers, bundle, _resolver);
if (_stat != null)
_stat.end();
}
return null;
}
@ -178,7 +186,12 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
ClassNameResolver classNameResolver = createClassNameResolver(context);
if (_parserTasks != null)
_parserTasks.add(new BundleParserTask(parser, handlers, bundleRes, classNameResolver));
{
BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes, classNameResolver);
_parserTasks.add(task);
if (LOG.isDebugEnabled())
task.setStatistic(new TimeStatistic());
}
}
/**

View File

@ -77,6 +77,10 @@ public class TestJettyOSGiBootWebAppAsService
options.add(CoreOptions.junitBundles());
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
"com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
"com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
String logLevel = "WARN";
@ -123,7 +127,7 @@ public class TestJettyOSGiBootWebAppAsService
//jsp bundles
res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.apache.taglibs.standard.glassfish").versionAsInProject());
res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
res.add(mavenBundle().groupId("org.glassfish").artifactId("javax.el").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart());

View File

@ -42,6 +42,7 @@ import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
@ -70,6 +71,10 @@ public class TestJettyOSGiBootWithJsp
options.add(CoreOptions.junitBundles());
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
"com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
"com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
String logLevel = "WARN";
@ -139,7 +144,7 @@ public class TestJettyOSGiBootWithJsp
//jetty jsp bundles
res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.apache.taglibs.standard.glassfish").versionAsInProject());
res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
res.add(mavenBundle().groupId("org.glassfish").artifactId("javax.el").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart());
@ -155,7 +160,7 @@ public class TestJettyOSGiBootWithJsp
@Test
public void assertAllBundlesActiveOrResolved()
{
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
}
// at the moment can't run httpservice with jsp at the same time.
@ -167,6 +172,7 @@ public class TestJettyOSGiBootWithJsp
TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
}
@Test
public void testJspDump() throws Exception
{

View File

@ -79,13 +79,12 @@
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">false</Set>
<Set name="outputBufferSize"><Property name="jetty.output.buffer.size" default="32768" /></Set>
<Set name="requestHeaderSize"><Property name="jetty.request.header.size" default="8192" /></Set>
<Set name="responseHeaderSize"><Property name="jetty.response.header.size" default="8192" /></Set>
<Set name="sendServerVersion"><Property name="jetty.send.server.version" default="true" /></Set>
<Set name="sendDateHeader"><Property name="jetty.send.date.header" default="false" /></Set>
<Set name="headerCacheSize">512</Set>
<!-- Uncomment to enable handling of X-Forwarded- style headers
<Call name="addCustomizer">
<Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>

View File

@ -21,13 +21,25 @@ lib/jetty-io-${jetty.version}.jar
etc/jetty.xml
[ini-template]
##
## Server Threading Configuration
##
# minimum number of threads
threads.min=10
# maximum number of threads
threads.max=200
# thread idle timeout in milliseconds
threads.timeout=60000
# buffer size for output
jetty.output.buffer.size=32768
# request header buffer size
jetty.request.header.size=8192
# response header buffer size
jetty.response.header.size=8192
# should jetty send the server version header?
jetty.send.server.version=true
# should jetty send the date header?
jetty.send.date.header=false
# What host to listen on (leave commented to listen on all interfaces)
#jetty.host=myhost.com
# Dump the state of the Jetty server, components, and webapps after startup

View File

@ -189,9 +189,10 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
for (ConnectionFactory factory:factories)
addConnectionFactory(factory);
if (acceptors<0)
acceptors=Math.max(1,(Runtime.getRuntime().availableProcessors()) / 2);
if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
int cores = Runtime.getRuntime().availableProcessors();
if (acceptors < 0)
acceptors = 1 + cores / 16;
if (acceptors > 2 * cores)
LOG.warn("Acceptors should be <= 2*availableProcessors: " + this);
_acceptors = new Thread[acceptors];
}

View File

@ -19,7 +19,7 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.util.Objects;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
@ -29,38 +29,25 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* <p>{@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.</p>
* <p>{@link HttpInput} holds a queue of items passed to it by calls to {@link #content(T)}.</p>
* <p>{@link HttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
* but simply holds references to the item, thus the caller must organize for those buffers to valid while
* held by this class.</p>
* <p>To assist the caller, subclasses may override methods {@link #onContentQueued(T)},
* {@link #onContentConsumed(T)} and {@link #onAllContentConsumed()} that can be implemented so that the
* caller will know when buffers are queued and consumed.</p>
*/
/**
* @author gregw
*
* @param <T>
*/
/**
* @author gregw
*
* @param <T>
* {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.
* <p/>
* Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class
* maintains two states: the content state that tells whether there is content to consume and the EOF
* state that tells whether an EOF has arrived.
* Only once the content has been consumed the content state is moved to the EOF state.
*/
public abstract class HttpInput<T> extends ServletInputStream implements Runnable
{
private final static Logger LOG = Log.getLogger(HttpInput.class);
private final byte[] _oneByteBuffer = new byte[1];
private HttpChannelState _channelState;
private Throwable _onError;
private ReadListener _listener;
private boolean _notReady;
protected State _state = BLOCKING;
private State _eof=null;
private final Object _lock;
private HttpChannelState _channelState;
private ReadListener _listener;
private Throwable _onError;
private boolean _notReady;
private State _contentState = STREAM;
private State _eofState;
private long _contentRead;
protected HttpInput()
@ -70,7 +57,15 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
protected HttpInput(Object lock)
{
_lock=lock==null?this:lock;
_lock = lock == null ? this : lock;
}
public void init(HttpChannelState state)
{
synchronized (lock())
{
_channelState = state;
}
}
public final Object lock()
@ -82,50 +77,15 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{
synchronized (lock())
{
_state = BLOCKING;
_eof=null;
_onError=null;
_contentRead=0;
_listener = null;
_onError = null;
_notReady = false;
_contentState = STREAM;
_eofState = null;
_contentRead = 0;
}
}
/**
* Access the next content to be consumed from. Returning the next item does not consume it
* and it may be returned multiple times until it is consumed. Calls to {@link #get(Object, byte[], int, int)}
* or {@link #consume(Object, int)} are required to consume data from the content.
* @return Content or null if none available.
* @throws IOException
*/
protected abstract T nextContent() throws IOException;
/**
* A convenience method to call nextContent and to check the return value, which if null then the
* a check is made for EOF and the state changed accordingly.
* @see #nextContent()
* @return Content or null if none available.
* @throws IOException
*/
protected T getNextContent() throws IOException
{
T content=nextContent();
if (content==null && _eof!=null)
{
LOG.debug("{} eof {}",this,_eof);
_state=_eof;
_eof=null;
}
return content;
}
@Override
public int read() throws IOException
{
int read = read(_oneByteBuffer, 0, 1);
return read < 0 ? -1 : 0xff & _oneByteBuffer[0];
}
@Override
public int available()
{
@ -134,7 +94,7 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
synchronized (lock())
{
T item = getNextContent();
return item==null?0:remaining(item);
return item == null ? 0 : remaining(item);
}
}
catch (IOException e)
@ -143,46 +103,115 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
}
}
@Override
public int read() throws IOException
{
int read = read(_oneByteBuffer, 0, 1);
return read < 0 ? -1 : _oneByteBuffer[0] & 0xFF;
}
@Override
public int read(byte[] b, int off, int len) throws IOException
{
T item = null;
int l;
synchronized (lock())
{
// System.err.printf("read s=%s q=%d e=%s%n",_state,_inputQ.size(),_eof);
// Get the current head of the input Q
item = getNextContent();
// If we have no item
T item = getNextContent();
if (item == null)
{
_state.waitForContent(this);
item=getNextContent();
if (item==null)
return _state.noContent();
_contentState.waitForContent(this);
item = getNextContent();
if (item == null)
return _contentState.noContent();
}
l=get(item, b, off, len);
_contentRead+=l;
int l = get(item, b, off, len);
_contentRead += l;
return l;
}
return l;
}
/**
* A convenience method to call nextContent and to check the return value, which if null then the
* a check is made for EOF and the state changed accordingly.
*
* @return Content or null if none available.
* @throws IOException
* @see #nextContent()
*/
protected T getNextContent() throws IOException
{
T content = nextContent();
if (content == null)
{
synchronized (lock())
{
if (_eofState != null)
{
LOG.debug("{} eof {}", this, _eofState);
_contentState = _eofState;
}
}
}
return content;
}
/**
* Access the next content to be consumed from. Returning the next item does not consume it
* and it may be returned multiple times until it is consumed.
* <p/>
* Calls to {@link #get(Object, byte[], int, int)}
* or {@link #consume(Object, int)} are required to consume data from the content.
*
* @return the content or null if none available.
* @throws IOException if retrieving the content fails
*/
protected abstract T nextContent() throws IOException;
/**
* @param item the content
* @return how many bytes remain in the given content
*/
protected abstract int remaining(T item);
/**
* Copies the given content into the given byte buffer.
*
* @param item the content to copy from
* @param buffer the buffer to copy into
* @param offset the buffer offset to start copying from
* @param length the space available in the buffer
* @return the number of bytes actually copied
*/
protected abstract int get(T item, byte[] buffer, int offset, int length);
/**
* Consumes the given content.
*
* @param item the content to consume
* @param length the number of bytes to consume
*/
protected abstract void consume(T item, int length);
/**
* Blocks until some content or some end-of-file event arrives.
*
* @throws IOException if the wait is interrupted
*/
protected abstract void blockForContent() throws IOException;
/**
* Adds some content to this input stream.
*
* @param item the content to add
*/
public abstract void content(T item);
protected boolean onAsyncRead()
{
if (_listener==null)
return false;
synchronized (lock())
{
if (_listener == null)
return false;
}
_channelState.onReadPossible();
return true;
}
@ -194,15 +223,11 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
return _contentRead;
}
}
/** Add some content to the input stream
* @param item
*/
public abstract void content(T item);
/** This method should be called to signal to the HttpInput
* that an EOF has arrived before all the expected content.
/**
* This method should be called to signal that an EOF has been
* detected before all the expected content arrived.
* <p/>
* Typically this will result in an EOFException being thrown
* from a subsequent read rather than a -1 return.
*/
@ -210,28 +235,34 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{
synchronized (lock())
{
if (_eof==null || !_eof.isEOF())
if (!isEOF())
{
LOG.debug("{} early EOF", this);
_eof=EARLY_EOF;
if (_listener!=null)
_channelState.onReadPossible();
_eofState = EARLY_EOF;
if (_listener == null)
return;
}
}
_channelState.onReadPossible();
}
/**
* This method should be called to signal that all the expected
* content arrived.
*/
public void messageComplete()
{
synchronized (lock())
{
if (_eof==null || !_eof.isEOF())
if (!isEOF())
{
LOG.debug("{} EOF", this);
_eof=EOF;
if (_listener!=null)
_channelState.onReadPossible();
_eofState = EOF;
if (_listener == null)
return;
}
}
_channelState.onReadPossible();
}
public void consumeAll()
@ -243,10 +274,10 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
while (!isFinished())
{
T item = getNextContent();
if (item==null)
_state.waitForContent(this);
if (item == null)
_contentState.waitForContent(this);
else
consume(item,remaining(item));
consume(item, remaining(item));
}
}
catch (IOException e)
@ -256,35 +287,48 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
}
}
/**
* @return whether an EOF has been detected, even though there may be content to consume.
*/
public boolean isEOF()
{
synchronized (lock())
{
return _eofState != null && _eofState.isEOF();
}
}
@Override
public boolean isFinished()
{
synchronized (lock())
{
return _state.isEOF();
return _contentState.isEOF();
}
}
@Override
public boolean isReady()
{
boolean finished;
synchronized (lock())
{
if (_listener==null)
if (_contentState.isEOF())
return true;
int available = available();
if (available>0)
if (_listener == null )
return true;
if (!_notReady)
{
_notReady=true;
if (_state.isEOF())
_channelState.onReadPossible();
else
unready();
}
return false;
if (available() > 0)
return true;
if (_notReady)
return false;
_notReady = true;
finished = isFinished();
}
if (finished)
_channelState.onReadPossible();
else
unready();
return false;
}
protected void unready()
@ -294,80 +338,79 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
@Override
public void setReadListener(ReadListener readListener)
{
if (readListener==null)
throw new NullPointerException("readListener==null");
readListener = Objects.requireNonNull(readListener);
synchronized (lock())
{
if (_state!=BLOCKING)
throw new IllegalStateException("state="+_state);
_state=ASYNC;
_listener=readListener;
_notReady=true;
_channelState.onReadPossible();
if (_contentState != STREAM)
throw new IllegalStateException("state=" + _contentState);
_contentState = ASYNC;
_listener = readListener;
_notReady = true;
}
_channelState.onReadPossible();
}
public void failed(Throwable x)
{
synchronized (lock())
{
if (_onError==null)
if (_onError == null)
LOG.warn(x);
else
_onError=x;
_onError = x;
}
}
@Override
public void run()
{
final boolean available;
final Throwable error;
final ReadListener listener;
boolean available = false;
final boolean eof;
final Throwable x;
synchronized (lock())
{
if (!_notReady || _listener==null)
if (!_notReady || _listener == null)
return;
x=_onError;
T item;
error = _onError;
listener = _listener;
try
{
item = getNextContent();
T item = getNextContent();
available = item != null && remaining(item) > 0;
}
catch(Exception e)
catch (Exception e)
{
item=null;
failed(e);
}
available= item!=null && remaining(item)>0;
eof = !available && _state.isEOF();
_notReady=!available&&!eof;
eof = !available && isFinished();
_notReady = !available && !eof;
}
try
{
if (x!=null)
_listener.onError(x);
if (error != null)
listener.onError(error);
else if (available)
_listener.onDataAvailable();
listener.onDataAvailable();
else if (eof)
_listener.onAllDataRead();
listener.onAllDataRead();
else
unready();
}
catch(Throwable e)
catch (Throwable e)
{
LOG.warn(e.toString());
LOG.debug(e);
_listener.onError(e);
listener.onError(e);
}
}
protected static class State
protected static abstract class State
{
public void waitForContent(HttpInput<?> in) throws IOException
{
@ -384,26 +427,28 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
}
}
protected static final State BLOCKING= new State()
protected static final State STREAM = new State()
{
@Override
public void waitForContent(HttpInput<?> in) throws IOException
public void waitForContent(HttpInput<?> input) throws IOException
{
in.blockForContent();
input.blockForContent();
}
public String toString()
{
return "OPEN";
return "STREAM";
}
};
protected static final State ASYNC= new State()
protected static final State ASYNC = new State()
{
@Override
public int noContent() throws IOException
{
return 0;
}
@Override
public String toString()
{
@ -411,25 +456,27 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
}
};
protected static final State EARLY_EOF= new State()
protected static final State EARLY_EOF = new State()
{
@Override
public int noContent() throws IOException
{
throw new EofException();
}
@Override
public boolean isEOF()
{
return true;
}
public String toString()
{
return "EARLY_EOF";
}
};
protected static final State EOF= new State()
protected static final State EOF = new State()
{
@Override
public boolean isEOF()
@ -442,13 +489,4 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
return "EOF";
}
};
public void init(HttpChannelState state)
{
synchronized (lock())
{
_channelState=state;
}
}
}

View File

@ -63,13 +63,15 @@ public class HttpOutput extends ServletOutputStream implements Runnable
private volatile Throwable _onError;
/*
ACTION OPEN ASYNC READY PENDING UNREADY
-------------------------------------------------------------------------------
setWriteListener() READY->owp ise ise ise ise
write() OPEN ise PENDING wpe wpe
flush() OPEN ise PENDING wpe wpe
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false
write completed - - - ASYNC READY->owp
ACTION OPEN ASYNC READY PENDING UNREADY CLOSED
-----------------------------------------------------------------------------------------------------
setWriteListener() READY->owp ise ise ise ise ise
write() OPEN ise PENDING wpe wpe eof
flush() OPEN ise PENDING wpe wpe eof
close() CLOSED CLOSED CLOSED CLOSED wpe CLOSED
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false CLOSED:true
write completed - - - ASYNC READY->owp -
*/
enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, CLOSED }
private final AtomicReference<OutputState> _state=new AtomicReference<>(OutputState.OPEN);
@ -131,48 +133,66 @@ public class HttpOutput extends ServletOutputStream implements Runnable
@Override
public void close()
{
OutputState state=_state.get();
while(state!=OutputState.CLOSED)
loop: while(true)
{
if (_state.compareAndSet(state,OutputState.CLOSED))
OutputState state=_state.get();
switch (state)
{
try
{
write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
}
catch(IOException e)
{
LOG.debug(e);
_channel.failed();
}
releaseBuffer();
return;
case CLOSED:
break loop;
case UNREADY:
throw new WritePendingException(); // TODO ?
default:
if (_state.compareAndSet(state,OutputState.CLOSED))
{
try
{
write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
}
catch(IOException e)
{
LOG.debug(e);
_channel.failed();
}
releaseBuffer();
return;
}
}
state=_state.get();
}
}
/* Called to indicated that the output is already closed and the state needs to be updated to match */
void closed()
{
OutputState state=_state.get();
while(state!=OutputState.CLOSED)
loop: while(true)
{
if (_state.compareAndSet(state,OutputState.CLOSED))
OutputState state=_state.get();
switch (state)
{
try
{
_channel.getResponse().closeOutput();
}
catch(IOException e)
{
LOG.debug(e);
_channel.failed();
}
releaseBuffer();
return;
case CLOSED:
break loop;
case UNREADY:
throw new WritePendingException(); // TODO ?
default:
if (_state.compareAndSet(state,OutputState.CLOSED))
{
try
{
_channel.getResponse().closeOutput();
}
catch(IOException e)
{
LOG.debug(e);
_channel.failed();
}
releaseBuffer();
return;
}
}
state=_state.get();
}
}
@ -667,8 +687,9 @@ public class HttpOutput extends ServletOutputStream implements Runnable
return false;
case UNREADY:
return false;
case CLOSED:
return false;
return true;
}
}
}
@ -680,23 +701,49 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
Throwable th=_onError;
_onError=null;
_writeListener.onError(th);
_writeListener.onError(new IOException(th));
close();
}
if (_state.get()==OutputState.READY)
switch(_state.get())
{
try
{
_writeListener.onWritePossible();
}
catch (Throwable e)
{
_writeListener.onError(e);
close();
}
case READY:
try
{
_writeListener.onWritePossible();
}
catch (Throwable e)
{
_writeListener.onError(e);
close();
}
break;
case CLOSED:
try
{
new Throwable().printStackTrace();
// even though a write is not possible, because a close has
// occurred, we need to call onWritePossible to tell async
// producer that the last write completed.
_writeListener.onWritePossible();
}
catch (Throwable e)
{
_writeListener.onError(e);
}
break;
default:
}
}
@Override
public String toString()
{
return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),_state.get());
}
private abstract class AsyncICB extends IteratingCallback
{
@ -722,7 +769,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
break;
case CLOSED:
_onError=new EofException("Closed");
break;
default:

View File

@ -0,0 +1,93 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* <p>A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.</p>
* <p>{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has
* been started without causing {@link java.util.ConcurrentModificationException}s.</p>
*/
public class NetworkTrafficServerConnector extends ServerConnector
{
private final List<NetworkTrafficListener> listeners = new CopyOnWriteArrayList<>();
public NetworkTrafficServerConnector(Server server)
{
this(server, null, null, null, 0, 0, new HttpConnectionFactory());
}
public NetworkTrafficServerConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory)
{
super(server, sslContextFactory, connectionFactory);
}
public NetworkTrafficServerConnector(Server server, ConnectionFactory connectionFactory)
{
super(server, connectionFactory);
}
public NetworkTrafficServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories)
{
super(server, executor, scheduler, pool, acceptors, selectors, factories);
}
public NetworkTrafficServerConnector(Server server, SslContextFactory sslContextFactory)
{
super(server, sslContextFactory);
}
/**
* @param listener the listener to add
*/
public void addNetworkTrafficListener(NetworkTrafficListener listener)
{
listeners.add(listener);
}
/**
* @param listener the listener to remove
*/
public void removeNetworkTrafficListener(NetworkTrafficListener listener)
{
listeners.remove(listener);
}
@Override
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key) throws IOException
{
NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
endPoint.notifyOpened();
return endPoint;
}
}

View File

@ -21,44 +21,58 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.InterruptedIOException;
import javax.servlet.ServletInputStream;
import org.eclipse.jetty.util.ArrayQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* <p>{@link QueuedHttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.</p>
* <p>{@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.</p>
* <p>{@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
* {@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.
* <p/>
* {@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
* but simply holds references to the item, thus the caller must organize for those buffers to valid while
* held by this class.</p>
* <p>To assist the caller, subclasses may override methods {@link #onAsyncRead()},
* {@link #onContentConsumed(Object)} and {@link #onAllContentConsumed()} that can be implemented so that the
* caller will know when buffers are queued and consumed.</p>
* held by this class.
* <p/>
* To assist the caller, subclasses may override methods {@link #onAsyncRead()}, {@link #onContentConsumed(Object)}
* that can be implemented so that the caller will know when buffers are queued and consumed.
*/
public abstract class QueuedHttpInput<T> extends HttpInput<T>
{
private final static Logger LOG = Log.getLogger(QueuedHttpInput.class);
private final ArrayQueue<T> _inputQ = new ArrayQueue<>(lock());
public QueuedHttpInput()
{}
{
}
public void content(T item)
{
// The buffer is not copied here. This relies on the caller not recycling the buffer
// until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are
// the signals to the caller that the buffers can be recycled.
synchronized (lock())
{
boolean wasEmpty = _inputQ.isEmpty();
_inputQ.add(item);
LOG.debug("{} queued {}", this, item);
if (wasEmpty)
{
if (!onAsyncRead())
lock().notify();
}
}
}
public void recycle()
{
synchronized (lock())
{
T item = _inputQ.peekUnsafe();
T item = _inputQ.pollUnsafe();
while (item != null)
{
_inputQ.pollUnsafe();
onContentConsumed(item);
item = _inputQ.peekUnsafe();
if (item == null)
onAllContentConsumed();
item = _inputQ.pollUnsafe();
}
super.recycle();
}
@ -67,30 +81,27 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
@Override
protected T nextContent()
{
T item = _inputQ.peekUnsafe();
// Skip empty items at the head of the queue
while (item != null && remaining(item) == 0)
synchronized (lock())
{
_inputQ.pollUnsafe();
onContentConsumed(item);
LOG.debug("{} consumed {}", this, item);
item = _inputQ.peekUnsafe();
// If that was the last item then notify
if (item==null)
onAllContentConsumed();
// Items are removed only when they are fully consumed.
T item = _inputQ.peekUnsafe();
// Skip consumed items at the head of the queue.
while (item != null && remaining(item) == 0)
{
_inputQ.pollUnsafe();
onContentConsumed(item);
LOG.debug("{} consumed {}", this, item);
item = _inputQ.peekUnsafe();
}
return item;
}
return item;
}
protected abstract void onContentConsumed(T item);
protected void blockForContent() throws IOException
{
synchronized (lock())
{
while (_inputQ.isEmpty() && !_state.isEOF())
while (_inputQ.isEmpty() && !isFinished() && !isEOF())
{
try
{
@ -105,40 +116,12 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
}
}
/* ------------------------------------------------------------ */
/** Called by this HttpInput to signal all available content has been consumed
/**
* Callback that signals that the given content has been consumed.
*
* @param item the consumed content
*/
protected void onAllContentConsumed()
{
}
/* ------------------------------------------------------------ */
/** Add some content to the input stream
* @param item
*/
public void content(T item)
{
// The buffer is not copied here. This relies on the caller not recycling the buffer
// until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are
// the signals to the caller that the buffers can be recycled.
synchronized (lock())
{
boolean empty=_inputQ.isEmpty();
_inputQ.add(item);
if (empty)
{
if (!onAsyncRead())
lock().notify();
}
LOG.debug("{} queued {}", this, item);
}
}
protected abstract void onContentConsumed(T item);
public void earlyEOF()
{
@ -148,7 +131,7 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
lock().notify();
}
}
public void messageComplete()
{
synchronized (lock())
@ -157,5 +140,4 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
lock().notify();
}
}
}

View File

@ -208,7 +208,7 @@ public class ServerConnector extends AbstractNetworkConnector
@Name("factories") ConnectionFactory... factories)
{
super(server,executor,scheduler,bufferPool,acceptors,factories);
_manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors >= 0 ? selectors : Runtime.getRuntime().availableProcessors());
_manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors > 0 ? selectors : Runtime.getRuntime().availableProcessors());
addBean(_manager, true);
}

View File

@ -21,6 +21,8 @@ package org.eclipse.jetty.server.handler;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
@ -2258,11 +2260,46 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
}
@Override
public ClassLoader getClassLoader()
{
AccessController.checkPermission(new RuntimePermission("getClassLoader"));
return _classLoader;
@Override
public ClassLoader getClassLoader()
{
if (!_enabled)
throw new UnsupportedOperationException();
//no security manager just return the classloader
if (System.getSecurityManager() == null)
return _classLoader;
else
{
//check to see if the classloader of the caller is the same as the context
//classloader, or a parent of it
try
{
Class reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
Class caller = (Class)getCallerClass.invoke(null, 2);
boolean ok = false;
ClassLoader callerLoader = caller.getClassLoader();
while (!ok && callerLoader != null)
{
if (callerLoader == _classLoader)
ok = true;
else
callerLoader = callerLoader.getParent();
}
if (ok)
return _classLoader;
}
catch (Exception e)
{
LOG.warn("Unable to check classloader of caller",e);
}
AccessController.checkPermission(new RuntimePermission("getClassLoader"));
return _classLoader;
}
}
@Override

View File

@ -18,83 +18,43 @@
package org.eclipse.jetty.server.nio;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NetworkTrafficServerConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* <p>A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.</p>
* <p>{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has
* been started without causing {@link ConcurrentModificationException}s.</p>
* @deprecated use {@link org.eclipse.jetty.server.NetworkTrafficServerConnector} instead.
*/
public class NetworkTrafficSelectChannelConnector extends ServerConnector
@Deprecated
public class NetworkTrafficSelectChannelConnector extends NetworkTrafficServerConnector
{
private final List<NetworkTrafficListener> listeners = new CopyOnWriteArrayList<NetworkTrafficListener>();
public NetworkTrafficSelectChannelConnector(Server server)
{
this(server,null,null,null,0,0,new HttpConnectionFactory());
super(server);
}
public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory)
{
super(server,sslContextFactory,connectionFactory);
super(server, connectionFactory, sslContextFactory);
}
public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory)
{
super(server,connectionFactory);
super(server, connectionFactory);
}
public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors,
ConnectionFactory... factories)
public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories)
{
super(server,executor,scheduler,pool,acceptors,selectors,factories);
super(server, executor, scheduler, pool, acceptors, selectors, factories);
}
public NetworkTrafficSelectChannelConnector(Server server, SslContextFactory sslContextFactory)
{
super(server,sslContextFactory);
super(server, sslContextFactory);
}
/**
* @param listener the listener to add
*/
public void addNetworkTrafficListener(NetworkTrafficListener listener)
{
listeners.add(listener);
}
/**
* @param listener the listener to remove
*/
public void removeNetworkTrafficListener(NetworkTrafficListener listener)
{
listeners.remove(listener);
}
@Override
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key) throws IOException
{
NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
endPoint.notifyOpened();
return endPoint;
}
}

View File

@ -50,6 +50,12 @@ public class HashedSession extends AbstractSession
* due to serialization failures that are most likely caused by user
* data stored in the session that is not serializable. */
private transient boolean _saveFailed = false;
/**
* True if an attempt has been made to de-idle a session and it failed. Once
* true, the session will not be attempted to be de-idled again.
*/
private transient boolean _deIdleFailed = false;
/* ------------------------------------------------------------- */
protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request)
@ -68,7 +74,7 @@ public class HashedSession extends AbstractSession
/* ------------------------------------------------------------- */
protected void checkValid()
{
if (_hashSessionManager._idleSavePeriodMs!=0)
if (!_deIdleFailed && _hashSessionManager._idleSavePeriodMs!=0)
deIdle();
super.checkValid();
}
@ -196,7 +202,7 @@ public class HashedSession extends AbstractSession
/* ------------------------------------------------------------ */
public synchronized void deIdle()
{
if (isIdled())
if (isIdled() && !_deIdleFailed)
{
// Access now to prevent race with idling period
access(System.currentTimeMillis());
@ -225,6 +231,7 @@ public class HashedSession extends AbstractSession
}
catch (Exception e)
{
deIdleFailed();
LOG.warn("Problem de-idling session " + super.getId(), e);
if (fis != null) IO.close(fis);//Must ensure closed before invalidate
invalidate();
@ -265,4 +272,15 @@ public class HashedSession extends AbstractSession
_saveFailed = true;
}
/* ------------------------------------------------------------ */
public synchronized void deIdleFailed()
{
_deIdleFailed = true;
}
/* ------------------------------------------------------------ */
public synchronized boolean isDeIdleFailed()
{
return _deIdleFailed;
}
}

View File

@ -299,7 +299,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
if (_dbAdaptor == null)
throw new IllegalStateException ("No DBAdaptor");
String longType = _dbAdaptor.getLongType();
return "alter table "+getTableName()+" add "+longType+" not null default "+MAX_INTERVAL_NOT_SET;
return "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType+" not null default "+MAX_INTERVAL_NOT_SET;
}
private void checkNotNull(String s)

View File

@ -95,49 +95,49 @@ public class JDBCSessionManager extends AbstractSessionManager
/**
* If dirty, session needs to be (re)persisted
*/
private boolean _dirty=false;
protected boolean _dirty=false;
/**
* Time in msec since the epoch that a session cookie was set for this session
*/
private long _cookieSet;
protected long _cookieSet;
/**
* Time in msec since the epoch that the session will expire
*/
private long _expiryTime;
protected long _expiryTime;
/**
* Time in msec since the epoch that the session was last persisted
*/
private long _lastSaved;
protected long _lastSaved;
/**
* Unique identifier of the last node to host the session
*/
private String _lastNode;
protected String _lastNode;
/**
* Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
*/
private String _virtualHost;
protected String _virtualHost;
/**
* Unique row in db for session
*/
private String _rowId;
protected String _rowId;
/**
* Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
*/
private String _canonicalContext;
protected String _canonicalContext;
/**
@ -246,7 +246,8 @@ public class JDBCSessionManager extends AbstractSessionManager
@Override
public void setAttribute (String name, Object value)
{
_dirty = (updateAttribute(name, value) || _dirty);
updateAttribute(name, value);
_dirty = true;
}
@Override
@ -494,6 +495,7 @@ public class JDBCSessionManager extends AbstractSessionManager
" interval="+(_saveIntervalSec * 1000L));
else
LOG.debug("getSession("+idInCluster+"): in session map, "+
" hashcode="+memSession.hashCode()+
" now="+now+
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
" interval="+(_saveIntervalSec * 1000L)+
@ -565,7 +567,11 @@ public class JDBCSessionManager extends AbstractSessionManager
}
else
{
//the session loaded from the db and the one in memory are the same, so keep using the one in memory
session = memSession;
LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
}
}
else
{
@ -769,6 +775,20 @@ public class JDBCSessionManager extends AbstractSessionManager
{
return new Session(request);
}
/**
* @param sessionId
* @param rowId
* @param created
* @param accessed
* @param maxInterval
* @return
*/
protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval)
{
return new Session(sessionId, rowId, created, accessed, maxInterval);
}
/* ------------------------------------------------------------ */
/** Remove session from manager
@ -892,7 +912,7 @@ public class JDBCSessionManager extends AbstractSessionManager
{
maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager
}
session = new Session(id, result.getString(_sessionTableSchema.getRowIdColumn()),
session = (Session)newSession(id, result.getString(_sessionTableSchema.getRowIdColumn()),
result.getLong(_sessionTableSchema.getCreateTimeColumn()),
result.getLong(_sessionTableSchema.getAccessTimeColumn()),
maxInterval);
@ -963,7 +983,7 @@ public class JDBCSessionManager extends AbstractSessionManager
long now = System.currentTimeMillis();
connection.setAutoCommit(true);
statement.setString(1, rowId); //rowId
statement.setString(2, session.getId()); //session id
statement.setString(2, session.getClusterId()); //session id
statement.setString(3, session.getCanonicalContext()); //context path
statement.setString(4, session.getVirtualHost()); //first vhost
statement.setString(5, getSessionIdManager().getWorkerName());//my node id
@ -1011,7 +1031,7 @@ public class JDBCSessionManager extends AbstractSessionManager
{
long now = System.currentTimeMillis();
connection.setAutoCommit(true);
statement.setString(1, data.getId());
statement.setString(1, data.getClusterId());
statement.setString(2, getSessionIdManager().getWorkerName());//my node id
statement.setLong(3, data.getAccessed());//accessTime
statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime
@ -1085,7 +1105,7 @@ public class JDBCSessionManager extends AbstractSessionManager
data.setLastSaved(now);
}
if (LOG.isDebugEnabled())
LOG.debug("Updated access time session id="+data.getId());
LOG.debug("Updated access time session id="+data.getId()+" with lastsaved="+data.getLastSaved());
}

View File

@ -348,7 +348,7 @@ public class HttpOutputTest
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteLargeKnown() throws Exception
{
@ -546,6 +546,22 @@ public class HttpOutputTest
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteSimpleKnown() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/simple.txt");
_handler._async=true;
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[4000];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length: 11"));
assertThat(response,containsString("simple text"));
}
static class ContentHandler extends AbstractHandler
{
@ -664,7 +680,6 @@ public class HttpOutputTest
BufferUtil.flipToFlush(_byteBuffer,0);
out.write(_byteBuffer);
}
Assert.assertFalse(out.isReady());
}
@Override

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -31,7 +28,6 @@ import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
@ -39,25 +35,27 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector;
import org.eclipse.jetty.util.BufferUtil;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@Ignore
public class NetworkTrafficListenerTest
{
private static final byte END_OF_CONTENT = '~';
private Server server;
private NetworkTrafficSelectChannelConnector connector;
private NetworkTrafficServerConnector connector;
public void initConnector(Handler handler) throws Exception
{
server = new Server();
connector = new NetworkTrafficSelectChannelConnector(server);
connector = new NetworkTrafficServerConnector(server);
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
server.addConnector(connector);
@ -121,9 +119,9 @@ public class NetworkTrafficListenerTest
}
});
final AtomicReference<String> incomingData = new AtomicReference<String>();
final AtomicReference<String> incomingData = new AtomicReference<>();
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@ -186,9 +184,9 @@ public class NetworkTrafficListenerTest
}
});
final AtomicReference<String> incomingData = new AtomicReference<String>();
final AtomicReference<String> incomingData = new AtomicReference<>();
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(2);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@ -253,9 +251,9 @@ public class NetworkTrafficListenerTest
}
});
final AtomicReference<String> incomingData = new AtomicReference<String>();
final AtomicReference<String> incomingData = new AtomicReference<>();
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(4);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@ -319,9 +317,9 @@ public class NetworkTrafficListenerTest
}
});
final AtomicReference<String> incomingData = new AtomicReference<String>();
final AtomicReference<String> incomingData = new AtomicReference<>();
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@ -393,8 +391,8 @@ public class NetworkTrafficListenerTest
}
});
final AtomicReference<String> incomingData = new AtomicReference<String>("");
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
final AtomicReference<String> incomingData = new AtomicReference<>("");
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{

View File

@ -16,11 +16,18 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
package org.eclipse.jetty.server;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.junit.Test;
public class MuxResponse extends UpgradeResponse
public class QueuedHttpInputTest
{
@Test
public void testNoContentMessageComplete() throws Exception
{
ByteBufferQueuedHttpInput input = new ByteBufferQueuedHttpInput();
input.messageComplete();
input.getNextContent();
}
}

View File

@ -319,7 +319,7 @@ public class ServletHandler extends ScopedHandler
}
/* ------------------------------------------------------------ */
IdentityService getIdentityService()
protected IdentityService getIdentityService()
{
return _identityService;
}
@ -647,7 +647,7 @@ public class ServletHandler extends ScopedHandler
}
/* ------------------------------------------------------------ */
private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
{
String key=pathInContext==null?servletHolder.getName():pathInContext;
int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
@ -735,7 +735,7 @@ public class ServletHandler extends ScopedHandler
}
/* ------------------------------------------------------------ */
private void invalidateChainsCache()
protected void invalidateChainsCache()
{
if (_chainLRU[FilterMapping.REQUEST]!=null)
{

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.servlet;
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@ -28,14 +26,18 @@ import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@ -52,12 +54,14 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
// TODO need these on SPDY as well!
public class AsyncServletIOTest
{
protected AsyncIOServlet _servlet=new AsyncIOServlet();
protected AsyncIOServlet _servlet0=new AsyncIOServlet();
protected AsyncIOServlet2 _servlet2=new AsyncIOServlet2();
protected int _port;
protected Server _server = new Server();
protected ServletHandler _servletHandler;
protected ServerConnector _connector;
@ -70,13 +74,20 @@ public class AsyncServletIOTest
_connector = new ServerConnector(_server,new HttpConnectionFactory(http_config));
_server.setConnectors(new Connector[]{ _connector });
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/ctx");
_server.setHandler(context);
_servletHandler=context.getServletHandler();
ServletHolder holder=new ServletHolder(_servlet);
ServletHolder holder=new ServletHolder(_servlet0);
holder.setAsyncSupported(true);
_servletHandler.addServletWithMapping(holder,"/path/*");
ServletHolder holder2=new ServletHolder(_servlet2);
holder.setAsyncSupported(true);
_servletHandler.addServletWithMapping(holder2,"/path2/*");
_server.start();
_port=_connector.getLocalPort();
@ -145,16 +156,53 @@ public class AsyncServletIOTest
{
process("Hello!!!\r\n",10);
}
protected void assertContains(String content,String response)
@Test
public void testAsync2() throws Exception
{
Assert.assertThat(response,Matchers.containsString(content));
}
protected void assertNotContains(String content,String response)
{
Assert.assertThat(response,Matchers.not(Matchers.containsString(content)));
StringBuilder request = new StringBuilder(512);
request.append("GET /ctx/path2/info HTTP/1.1\r\n")
.append("Host: localhost\r\n")
.append("Connection: close\r\n")
.append("\r\n");
int port=_port;
List<String> list = new ArrayList<>();
try (Socket socket = new Socket("localhost",port))
{
socket.setSoTimeout(1000000);
OutputStream out = socket.getOutputStream();
out.write(request.toString().getBytes("ISO-8859-1"));
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()),102400);
// response line
String line = in.readLine();
// System.err.println("resp: "+line);
Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
// Skip headers
while (line!=null)
{
line = in.readLine();
// System.err.println("line: "+line);
if (line.length()==0)
break;
}
// Get body slowly
while (true)
{
line = in.readLine();
// System.err.println("body: "+line);
if (line==null)
break;
list.add(line);
}
}
Assert.assertEquals(list.get(0),"data");
Assert.assertTrue(_servlet2.completed.await(5, TimeUnit.SECONDS));
}
public synchronized List<String> process(String content,int... writes) throws Exception
@ -183,14 +231,14 @@ public class AsyncServletIOTest
.append("Connection: close\r\n");
if (content!=null)
request.append("Content-Length: "+content.length+"\r\n")
request.append("Content-Length: ").append(content.length).append("\r\n")
.append("Content-Type: text/plain\r\n");
request.append("\r\n");
int port=_port;
List<String> list = new ArrayList<>();
try (Socket socket = new Socket("localhost",port);)
try (Socket socket = new Socket("localhost",port))
{
socket.setSoTimeout(1000000);
OutputStream out = socket.getOutputStream();
@ -266,9 +314,9 @@ public class AsyncServletIOTest
private static final long serialVersionUID = -8161977157098646562L;
public AsyncIOServlet()
{}
{
}
/* ------------------------------------------------------------ */
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{
@ -298,7 +346,7 @@ public class AsyncServletIOTest
throw new IllegalStateException();
// System.err.println("ODA");
while (in.isReady())
while (in.isReady() && !in.isFinished())
{
_oda.incrementAndGet();
int len=in.read(_buf);
@ -374,4 +422,80 @@ public class AsyncServletIOTest
});
}
}
public class AsyncIOServlet2 extends HttpServlet
{
public CountDownLatch completed = new CountDownLatch(1);
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException
{
new SampleAsycListener(request,response);
}
class SampleAsycListener implements WriteListener, AsyncListener
{
final ServletResponse response;
final ServletOutputStream servletOutputStream;
final AsyncContext asyncContext;
volatile boolean written=false;
SampleAsycListener(HttpServletRequest request,HttpServletResponse response) throws IOException
{
asyncContext = request.startAsync();
asyncContext.setTimeout(10000L);
asyncContext.addListener(this);
servletOutputStream = response.getOutputStream();
servletOutputStream.setWriteListener(this);
this.response=response;
}
@Override
public void onWritePossible() throws IOException
{
if (!written)
{
written=true;
response.setContentLength(5);
servletOutputStream.write("data\n".getBytes());
}
if (servletOutputStream.isReady())
{
asyncContext.complete();
}
}
@Override
public void onError(final Throwable t)
{
t.printStackTrace();
asyncContext.complete();
}
@Override
public void onComplete(final AsyncEvent event) throws IOException
{
completed.countDown();
}
@Override
public void onTimeout(final AsyncEvent event) throws IOException
{
asyncContext.complete();
}
@Override
public void onError(final AsyncEvent event) throws IOException
{
asyncContext.complete();
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
}
}
}

View File

@ -392,6 +392,13 @@ public class SPDYClient
return Collections.unmodifiableCollection(sessions);
}
@Override
protected void dumpThis(Appendable out) throws IOException
{
super.dumpThis(out);
dump(out, "", sessions);
}
private class ClientSelectorManager extends SelectorManager
{
private ClientSelectorManager(Executor executor, Scheduler scheduler)

View File

@ -180,6 +180,7 @@ public class Flusher
// Has the stream been reset for this data frame ?
if (stream != null && stream.isReset() && frameBytes instanceof StandardSession.DataFrameBytes)
{
// TODO: notify from within sync block !
frameBytes.failed(new StreamException(frameBytes.getStream().getId(),
StreamStatus.INVALID_STREAM, "Stream: " + frameBytes.getStream() + " is reset!"));
continue;

View File

@ -0,0 +1,9 @@
[name]
npn-boot
[files]
http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar
[ini-template]
--exec
-Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.spdy.server;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Queue;
@ -182,6 +183,13 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
return Collections.unmodifiableCollection(sessions);
}
@Override
protected void dumpThis(Appendable out) throws IOException
{
super.dumpThis(out);
dump(out, "", sessions);
}
private class ServerSPDYConnection extends SPDYConnection implements Runnable
{
private final ServerSessionFrameListener listener;

View File

@ -57,12 +57,12 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.Scheduler;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Ignore;
import org.junit.Test;
public class SynDataReplyDataLoadTest extends AbstractTest
{
private static final int TIMEOUT = 60000;
private static final int TIMEOUT = 60 * 1000;
private static final Logger logger = Log.getLogger(SynDataReplyDataLoadTest.class);
@Test(timeout = TIMEOUT)
@ -104,14 +104,20 @@ public class SynDataReplyDataLoadTest extends AbstractTest
};
}
};
short spdyVersion = SPDY.V2;
long idleTimeout = 2 * TIMEOUT;
server = newServer();
connector = new ServerConnector(server, null, null, serverBufferPool, 1,
Runtime.getRuntime().availableProcessors() / 2, new SPDYServerConnectionFactory(SPDY.V3, listener));
Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
new SPDYServerConnectionFactory(spdyVersion, listener));
connector.setIdleTimeout(idleTimeout);
QueuedThreadPool clientExecutor = new QueuedThreadPool();
clientExecutor.setName(clientExecutor.getName() + "-client");
clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, 30000);
final Session session = startClient(SPDY.V3, startServer(SPDY.V3, listener), null);
clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, idleTimeout);
final Session session = startClient(spdyVersion, startServer(spdyVersion, listener), null);
final Thread testThread = Thread.currentThread();
Runnable timeout = new Runnable()
@ -162,7 +168,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
}
});
}
Scheduler.Task timeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
Scheduler.Task syncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
{
long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks);
@ -172,7 +178,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
long end = System.nanoTime();
System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
}
timeoutTask.cancel();
syncTimeoutTask.cancel();
tasks.clear();
for (int i = 0; i < count; ++i)
@ -187,7 +193,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
}
});
}
timeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
Scheduler.Task asyncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
{
long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks);
@ -197,7 +203,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest
long end = System.nanoTime();
System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
}
timeoutTask.cancel();
asyncTimeoutTask.cancel();
threadPool.shutdown();
Assert.assertEquals(0, leaks.get());
@ -206,7 +213,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
private void synCompletedData(Session session, Fields headers, int iterations) throws Exception
{
final Map<Integer, Integer> counter = new ConcurrentHashMap<>(iterations);
final CountDownLatch latch = new CountDownLatch(2 * iterations);
final CountDownLatch requestsLatch = new CountDownLatch(2 * iterations);
for (int i = 0; i < iterations; ++i)
{
final AtomicInteger count = new AtomicInteger(2);
@ -218,7 +225,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(2, count.getAndDecrement());
latch.countDown();
requestsLatch.countDown();
}
@Override
@ -230,7 +237,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
{
Assert.assertEquals(1, count.getAndDecrement());
counter.remove(index);
latch.countDown();
requestsLatch.countDown();
}
}
}, new Promise.Adapter<Stream>()
@ -244,7 +251,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
}
);
}
Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
Assert.assertTrue(requestsLatch.await(iterations, TimeUnit.SECONDS));
Assert.assertTrue(counter.toString(), counter.isEmpty());
}

View File

@ -1,2 +1,2 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.spdy.LEVEL=WARN
#org.eclipse.jetty.spdy.LEVEL=DEBUG

View File

@ -572,7 +572,7 @@ public class Main
args.setAllModules(modules);
List<Module> activeModules = modules.resolveEnabled();
// 7) Lib & XML Expansion / Resolution
args.expandModules(baseHome,activeModules);

View File

@ -39,6 +39,13 @@ import java.util.regex.Pattern;
public class Modules implements Iterable<Module>
{
private Map<String, Module> modules = new HashMap<>();
/*
* modules that may appear in the resolved graph but are undefined in the module system
*
* ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file)
*/
private Set<String> missingModules = new HashSet<String>();
private int maxDepth = -1;
private Set<String> asNameSet(Set<Module> moduleSet)
@ -110,7 +117,7 @@ public class Modules implements Iterable<Module>
if (parent == null)
{
System.err.printf("WARNING: module not found [%s]%n",parentName);
StartLog.debug("module not found [%s]%n",parentName);
}
else
{
@ -124,7 +131,7 @@ public class Modules implements Iterable<Module>
Module optional = get(optionalParentName);
if (optional == null)
{
System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
StartLog.debug("optional module not found [%s]%n",optionalParentName);
}
else if (optional.isEnabled())
{
@ -285,12 +292,12 @@ public class Modules implements Iterable<Module>
}
}
private void findParents(Module module, Set<Module> ret)
private void findParents(Module module, Map<String, Module> ret)
{
ret.add(module);
ret.put(module.getName(), module);
for (Module parent : module.getParentEdges())
{
ret.add(parent);
ret.put(parent.getName(), parent);
findParents(parent,ret);
}
}
@ -370,7 +377,7 @@ public class Modules implements Iterable<Module>
}
// load missing post-expanded dependent modules
boolean done = false;
boolean done = false;
while (!done)
{
done = true;
@ -380,7 +387,7 @@ public class Modules implements Iterable<Module>
{
for (String parent : m.getParentNames())
{
if (modules.containsKey(parent))
if (modules.containsKey(parent) || missingModules.contains(parent))
{
continue; // found. skip it.
}
@ -392,8 +399,16 @@ public class Modules implements Iterable<Module>
for (String missingParent : missingParents)
{
File file = basehome.getFile("modules/" + missingParent + ".mod");
Module module = registerModule(basehome,args,file);
updateParentReferencesTo(module);
if ( FS.canReadFile(file) )
{
Module module = registerModule(basehome,args,file);
updateParentReferencesTo(module);
}
else
{
StartLog.debug("Missing module definition: [ Mod: %s | File: %s]", missingParent, file);
missingModules.add(missingParent);
}
}
}
}
@ -425,7 +440,7 @@ public class Modules implements Iterable<Module>
*/
public List<Module> resolveEnabled()
{
Set<Module> active = new HashSet<Module>();
Map<String, Module> active = new HashMap<String,Module>();
for (Module module : modules.values())
{
@ -435,18 +450,37 @@ public class Modules implements Iterable<Module>
}
}
/*
* check against the missing modules
*
* Ex: npn should match anything under npn/
*/
for ( String missing : missingModules )
{
for (String activeModule: active.keySet())
{
if ( missing.startsWith(activeModule) )
{
StartLog.warn("** Unable to continue, required dependency missing. [%s]", missing);
StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
return Collections.emptyList();
}
}
}
List<Module> ordered = new ArrayList<>();
ordered.addAll(active);
ordered.addAll(active.values());
Collections.sort(ordered,new Module.DepthComparator());
return ordered;
}
public Set<String> resolveParentModulesOf(String moduleName)
{
Set<Module> ret = new HashSet<>();
Map<String,Module> ret = new HashMap<>();
Module module = get(moduleName);
findParents(module,ret);
return asNameSet(ret);
return ret.keySet();
}
private String toIndent(int depth)

View File

@ -98,8 +98,9 @@ public final class Props implements Iterable<Prop>
if (props.isEmpty())
{
// This is a stupid programming error, we should have something, even system properties
throw new PropsException("Props is empty: no properties declared!?");
// nothing to expand
// this situation can occur from --add-to-startd on a new blank base directory
return str;
}
Pattern pat = Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})");

View File

@ -1,145 +1,154 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.start;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Assert;
import org.junit.Test;
public class BaseHomeTest
{
private void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)
{
List<String> actual = new ArrayList<>();
for (File file : files)
{
actual.add(hb.toShortForm(file));
}
Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));
}
@Test
public void testGetFile_OnlyHome() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = null;
BaseHome hb = new BaseHome(homeDir,baseDir);
File startIni = hb.getFile("/start.ini");
String ref = hb.toShortForm(startIni);
Assert.assertThat("Reference",ref,startsWith("${jetty.home}"));
String contents = IO.readToString(startIni);
Assert.assertThat("Contents",contents,containsString("Home Ini"));
}
@Test
public void testListFiles_OnlyHome() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = null;
BaseHome hb = new BaseHome(homeDir,baseDir);
List<File> files = hb.listFiles("/start.d");
List<String> expected = new ArrayList<>();
expected.add("${jetty.home}/start.d/jmx.ini");
expected.add("${jetty.home}/start.d/jndi.ini");
expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.home}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini");
assertFileList(hb,"Files found",expected,files);
}
@Test
public void testListFiles_Filtered_OnlyHome() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = null;
BaseHome hb = new BaseHome(homeDir,baseDir);
List<File> files = hb.listFiles("/start.d",new FS.IniFilter());
List<String> expected = new ArrayList<>();
expected.add("${jetty.home}/start.d/jmx.ini");
expected.add("${jetty.home}/start.d/jndi.ini");
expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.home}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini");
assertFileList(hb,"Files found",expected,files);
}
@Test
public void testListFiles_Both() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
BaseHome hb = new BaseHome(homeDir,baseDir);
List<File> files = hb.listFiles("/start.d");
List<String> expected = new ArrayList<>();
expected.add("${jetty.base}/start.d/jmx.ini");
expected.add("${jetty.home}/start.d/jndi.ini");
expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.base}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini");
expected.add("${jetty.base}/start.d/myapp.ini");
assertFileList(hb,"Files found",expected,files);
}
@Test
public void testDefault() throws IOException
{
BaseHome bh = new BaseHome();
Assert.assertThat("Home",bh.getHome(),notNullValue());
Assert.assertThat("Base",bh.getBase(),notNullValue());
}
@Test
public void testGetFile_Both() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
BaseHome hb = new BaseHome(homeDir,baseDir);
File startIni = hb.getFile("/start.ini");
String ref = hb.toShortForm(startIni);
Assert.assertThat("Reference",ref,startsWith("${jetty.base}"));
String contents = IO.readToString(startIni);
Assert.assertThat("Contents",contents,containsString("Base Ini"));
}
}
//
// ========================================================================
// Copyright (c) 1995-2014 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.start;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Assert;
import org.junit.Test;
public class BaseHomeTest
{
private void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)
{
List<String> actual = new ArrayList<>();
for (File file : files)
{
actual.add(hb.toShortForm(file));
}
Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));
}
private void toOsSeparators(List<String> expected)
{
for (int i = 0; i < expected.size(); i++)
{
String fixed = FS.separators(expected.get(i));
expected.set(i,fixed);
}
}
@Test
public void testGetFile_OnlyHome() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = null;
BaseHome hb = new BaseHome(homeDir,baseDir);
File startIni = hb.getFile("/start.ini");
String ref = hb.toShortForm(startIni);
Assert.assertThat("Reference",ref,startsWith("${jetty.home}"));
String contents = IO.readToString(startIni);
Assert.assertThat("Contents",contents,containsString("Home Ini"));
}
@Test
public void testListFiles_OnlyHome() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = null;
BaseHome hb = new BaseHome(homeDir,baseDir);
List<File> files = hb.listFiles("/start.d");
List<String> expected = new ArrayList<>();
expected.add("${jetty.home}/start.d/jmx.ini");
expected.add("${jetty.home}/start.d/jndi.ini");
expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.home}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini");
toOsSeparators(expected);
assertFileList(hb,"Files found",expected,files);
}
@Test
public void testListFiles_Filtered_OnlyHome() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = null;
BaseHome hb = new BaseHome(homeDir,baseDir);
List<File> files = hb.listFiles("/start.d",new FS.IniFilter());
List<String> expected = new ArrayList<>();
expected.add("${jetty.home}/start.d/jmx.ini");
expected.add("${jetty.home}/start.d/jndi.ini");
expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.home}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini");
toOsSeparators(expected);
assertFileList(hb,"Files found",expected,files);
}
@Test
public void testListFiles_Both() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
BaseHome hb = new BaseHome(homeDir,baseDir);
List<File> files = hb.listFiles("/start.d");
List<String> expected = new ArrayList<>();
expected.add("${jetty.base}/start.d/jmx.ini");
expected.add("${jetty.home}/start.d/jndi.ini");
expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.base}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini");
expected.add("${jetty.base}/start.d/myapp.ini");
toOsSeparators(expected);
assertFileList(hb,"Files found",expected,files);
}
@Test
public void testDefault() throws IOException
{
BaseHome bh = new BaseHome();
Assert.assertThat("Home",bh.getHome(),notNullValue());
Assert.assertThat("Base",bh.getBase(),notNullValue());
}
@Test
public void testGetFile_Both() throws IOException
{
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
BaseHome hb = new BaseHome(homeDir,baseDir);
File startIni = hb.getFile("/start.ini");
String ref = hb.toShortForm(startIni);
Assert.assertThat("Reference",ref,startsWith("${jetty.base}"));
String contents = IO.readToString(startIni);
Assert.assertThat("Contents",contents,containsString("Base Ini"));
}
}

View File

@ -1,274 +1,274 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.start;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.start.Props.Prop;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Assert;
public class ConfigurationAssert
{
/**
* Given a provided StartArgs, assert that the configuration it has determined is valid based on values in a assert text file.
*
* @param baseHome
* the BaseHome used. Access it via {@link Main#getBaseHome()}
* @param args
* the StartArgs that has been processed via {@link Main#processCommandLine(String[])}
* @param filename
* the filename of the assertion values
* @throws IOException
*/
public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException
{
File testResourcesDir = MavenTestingUtils.getTestResourcesDir();
File file = MavenTestingUtils.getTestResourceFile(filename);
TextFile textFile = new TextFile(file);
// Validate XMLs (order is important)
List<String> expectedXmls = new ArrayList<>();
for (String line : textFile)
{
if (line.startsWith("XML|"))
{
expectedXmls.add(getValue(line));
}
}
List<String> actualXmls = new ArrayList<>();
for (File xml : args.getXmlFiles())
{
actualXmls.add(shorten(baseHome,xml,testResourcesDir));
}
assertOrdered("XML Resolution Order",expectedXmls,actualXmls);
// Validate LIBs (order is not important)
List<String> expectedLibs = new ArrayList<>();
for (String line : textFile)
{
if (line.startsWith("LIB|"))
{
expectedLibs.add(getValue(line));
}
}
List<String> actualLibs = new ArrayList<>();
for (File path : args.getClasspath())
{
actualLibs.add(shorten(baseHome,path,testResourcesDir));
}
assertContainsUnordered("Libs",expectedLibs,actualLibs);
// Validate PROPERTIES (order is not important)
Set<String> expectedProperties = new HashSet<>();
for (String line : textFile)
{
if (line.startsWith("PROP|"))
{
expectedProperties.add(getValue(line));
}
}
List<String> actualProperties = new ArrayList<>();
for (Prop prop : args.getProperties())
{
String name = prop.key;
if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))
{
// strip these out from assertion, to make assertions easier.
continue;
}
actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value));
}
assertContainsUnordered("Properties",expectedProperties,actualProperties);
// Validate Downloads
List<String> expectedDownloads = new ArrayList<>();
for (String line : textFile)
{
if (line.startsWith("DOWNLOAD|"))
{
expectedDownloads.add(getValue(line));
}
}
List<String> actualDownloads = new ArrayList<>();
for (FileArg darg : args.getFiles())
{
if (darg.uri != null)
{
actualDownloads.add(String.format("%s:%s",darg.uri,darg.location));
}
}
assertContainsUnordered("Downloads",expectedDownloads,actualDownloads);
// Validate Files/Dirs creation
List<String> expectedFiles = new ArrayList<>();
for(String line: textFile)
{
if(line.startsWith("FILE|"))
{
expectedFiles.add(getValue(line));
}
}
List<String> actualFiles = new ArrayList<>();
for(FileArg farg: args.getFiles())
{
if(farg.uri == null)
{
actualFiles.add(farg.location);
}
}
assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles);
}
private static String shorten(BaseHome baseHome, File path, File testResourcesDir)
{
String value = baseHome.toShortForm(path);
if (value.startsWith(testResourcesDir.getAbsolutePath()))
{
int len = testResourcesDir.getAbsolutePath().length();
value = "${maven-test-resources}" + value.substring(len);
}
return value;
}
private static void assertContainsUnordered(String msg, Collection<String> expectedSet, Collection<String> actualSet)
{
// same size?
boolean mismatch = expectedSet.size() != actualSet.size();
// test content
Set<String> missing = new HashSet<>();
for (String expected : expectedSet)
{
if (!actualSet.contains(expected))
{
missing.add(expected);
}
}
if (mismatch || missing.size() > 0)
{
// build up detailed error message
StringWriter message = new StringWriter();
PrintWriter err = new PrintWriter(message);
err.printf("%s: Assert Contains (Unordered)",msg);
if (mismatch)
{
err.print(" [size mismatch]");
}
if (missing.size() >= 0)
{
err.printf(" [%d entries missing]",missing.size());
}
err.println();
err.printf("Actual Entries (size: %d)%n",actualSet.size());
for (String actual : actualSet)
{
char indicator = expectedSet.contains(actual)?' ':'>';
err.printf("%s| %s%n",indicator,actual);
}
err.printf("Expected Entries (size: %d)%n",expectedSet.size());
for (String expected : expectedSet)
{
char indicator = actualSet.contains(expected)?' ':'>';
err.printf("%s| %s%n",indicator,expected);
}
err.flush();
Assert.fail(message.toString());
}
}
private static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)
{
// same size?
boolean mismatch = expectedList.size() != actualList.size();
// test content
List<Integer> badEntries = new ArrayList<>();
int min = Math.min(expectedList.size(),actualList.size());
int max = Math.max(expectedList.size(),actualList.size());
for (int i = 0; i < min; i++)
{
if (!expectedList.get(i).equals(actualList.get(i)))
{
badEntries.add(i);
}
}
for (int i = min; i < max; i++)
{
badEntries.add(i);
}
if (mismatch || badEntries.size() > 0)
{
// build up detailed error message
StringWriter message = new StringWriter();
PrintWriter err = new PrintWriter(message);
err.printf("%s: Assert Contains (Unordered)",msg);
if (mismatch)
{
err.print(" [size mismatch]");
}
if (badEntries.size() >= 0)
{
err.printf(" [%d entries not matched]",badEntries.size());
}
err.println();
err.printf("Actual Entries (size: %d)%n",actualList.size());
for (int i = 0; i < actualList.size(); i++)
{
String actual = actualList.get(i);
char indicator = badEntries.contains(i)?'>':' ';
err.printf("%s[%d] %s%n",indicator,i,actual);
}
err.printf("Expected Entries (size: %d)%n",expectedList.size());
for (int i = 0; i < expectedList.size(); i++)
{
String expected = expectedList.get(i);
char indicator = badEntries.contains(i)?'>':' ';
err.printf("%s[%d] %s%n",indicator,i,expected);
}
err.flush();
Assert.fail(message.toString());
}
}
private static String getValue(String arg)
{
int idx = arg.indexOf('|');
Assert.assertThat("Expecting '|' sign in [" + arg + "]",idx,greaterThanOrEqualTo(0));
String value = arg.substring(idx + 1).trim();
Assert.assertThat("Expecting Value after '|' in [" + arg + "]",value.length(),greaterThan(0));
return value;
}
}
//
// ========================================================================
// Copyright (c) 1995-2014 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.start;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.start.Props.Prop;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Assert;
public class ConfigurationAssert
{
/**
* Given a provided StartArgs, assert that the configuration it has determined is valid based on values in a assert text file.
*
* @param baseHome
* the BaseHome used. Access it via {@link Main#getBaseHome()}
* @param args
* the StartArgs that has been processed via {@link Main#processCommandLine(String[])}
* @param filename
* the filename of the assertion values
* @throws IOException
*/
public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException
{
File testResourcesDir = MavenTestingUtils.getTestResourcesDir();
File file = MavenTestingUtils.getTestResourceFile(filename);
TextFile textFile = new TextFile(file);
// Validate XMLs (order is important)
List<String> expectedXmls = new ArrayList<>();
for (String line : textFile)
{
if (line.startsWith("XML|"))
{
expectedXmls.add(FS.separators(getValue(line)));
}
}
List<String> actualXmls = new ArrayList<>();
for (File xml : args.getXmlFiles())
{
actualXmls.add(shorten(baseHome,xml,testResourcesDir));
}
assertOrdered("XML Resolution Order",expectedXmls,actualXmls);
// Validate LIBs (order is not important)
List<String> expectedLibs = new ArrayList<>();
for (String line : textFile)
{
if (line.startsWith("LIB|"))
{
expectedLibs.add(FS.separators(getValue(line)));
}
}
List<String> actualLibs = new ArrayList<>();
for (File path : args.getClasspath())
{
actualLibs.add(shorten(baseHome,path,testResourcesDir));
}
assertContainsUnordered("Libs",expectedLibs,actualLibs);
// Validate PROPERTIES (order is not important)
Set<String> expectedProperties = new HashSet<>();
for (String line : textFile)
{
if (line.startsWith("PROP|"))
{
expectedProperties.add(getValue(line));
}
}
List<String> actualProperties = new ArrayList<>();
for (Prop prop : args.getProperties())
{
String name = prop.key;
if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))
{
// strip these out from assertion, to make assertions easier.
continue;
}
actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value));
}
assertContainsUnordered("Properties",expectedProperties,actualProperties);
// Validate Downloads
List<String> expectedDownloads = new ArrayList<>();
for (String line : textFile)
{
if (line.startsWith("DOWNLOAD|"))
{
expectedDownloads.add(getValue(line));
}
}
List<String> actualDownloads = new ArrayList<>();
for (FileArg darg : args.getFiles())
{
if (darg.uri != null)
{
actualDownloads.add(String.format("%s:%s",darg.uri,darg.location));
}
}
assertContainsUnordered("Downloads",expectedDownloads,actualDownloads);
// Validate Files/Dirs creation
List<String> expectedFiles = new ArrayList<>();
for(String line: textFile)
{
if(line.startsWith("FILE|"))
{
expectedFiles.add(getValue(line));
}
}
List<String> actualFiles = new ArrayList<>();
for(FileArg farg: args.getFiles())
{
if(farg.uri == null)
{
actualFiles.add(farg.location);
}
}
assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles);
}
private static String shorten(BaseHome baseHome, File path, File testResourcesDir)
{
String value = baseHome.toShortForm(path);
if (value.startsWith(testResourcesDir.getAbsolutePath()))
{
int len = testResourcesDir.getAbsolutePath().length();
value = "${maven-test-resources}" + value.substring(len);
}
return value;
}
private static void assertContainsUnordered(String msg, Collection<String> expectedSet, Collection<String> actualSet)
{
// same size?
boolean mismatch = expectedSet.size() != actualSet.size();
// test content
Set<String> missing = new HashSet<>();
for (String expected : expectedSet)
{
if (!actualSet.contains(expected))
{
missing.add(expected);
}
}
if (mismatch || missing.size() > 0)
{
// build up detailed error message
StringWriter message = new StringWriter();
PrintWriter err = new PrintWriter(message);
err.printf("%s: Assert Contains (Unordered)",msg);
if (mismatch)
{
err.print(" [size mismatch]");
}
if (missing.size() >= 0)
{
err.printf(" [%d entries missing]",missing.size());
}
err.println();
err.printf("Actual Entries (size: %d)%n",actualSet.size());
for (String actual : actualSet)
{
char indicator = expectedSet.contains(actual)?' ':'>';
err.printf("%s| %s%n",indicator,actual);
}
err.printf("Expected Entries (size: %d)%n",expectedSet.size());
for (String expected : expectedSet)
{
char indicator = actualSet.contains(expected)?' ':'>';
err.printf("%s| %s%n",indicator,expected);
}
err.flush();
Assert.fail(message.toString());
}
}
private static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)
{
// same size?
boolean mismatch = expectedList.size() != actualList.size();
// test content
List<Integer> badEntries = new ArrayList<>();
int min = Math.min(expectedList.size(),actualList.size());
int max = Math.max(expectedList.size(),actualList.size());
for (int i = 0; i < min; i++)
{
if (!expectedList.get(i).equals(actualList.get(i)))
{
badEntries.add(i);
}
}
for (int i = min; i < max; i++)
{
badEntries.add(i);
}
if (mismatch || badEntries.size() > 0)
{
// build up detailed error message
StringWriter message = new StringWriter();
PrintWriter err = new PrintWriter(message);
err.printf("%s: Assert Contains (Unordered)",msg);
if (mismatch)
{
err.print(" [size mismatch]");
}
if (badEntries.size() >= 0)
{
err.printf(" [%d entries not matched]",badEntries.size());
}
err.println();
err.printf("Actual Entries (size: %d)%n",actualList.size());
for (int i = 0; i < actualList.size(); i++)
{
String actual = actualList.get(i);
char indicator = badEntries.contains(i)?'>':' ';
err.printf("%s[%d] %s%n",indicator,i,actual);
}
err.printf("Expected Entries (size: %d)%n",expectedList.size());
for (int i = 0; i < expectedList.size(); i++)
{
String expected = expectedList.get(i);
char indicator = badEntries.contains(i)?'>':' ';
err.printf("%s[%d] %s%n",indicator,i,expected);
}
err.flush();
Assert.fail(message.toString());
}
}
private static String getValue(String arg)
{
int idx = arg.indexOf('|');
Assert.assertThat("Expecting '|' sign in [" + arg + "]",idx,greaterThanOrEqualTo(0));
String value = arg.substring(idx + 1).trim();
Assert.assertThat("Expecting Value after '|' in [" + arg + "]",value.length(),greaterThan(0));
return value;
}
}

View File

@ -61,11 +61,24 @@ public class TestUseCases
assertUseCase("home","base.jmx","assert-jmx.txt");
}
@Test
public void testWithMissingNpnVersion() throws Exception
{
assertUseCase("home","base.missing.npn.version","assert-missing-npn-version.txt","java.version=1.7.0_01");
}
@Test
public void testWithSpdy() throws Exception
{
assertUseCase("home","base.enable.spdy","assert-enable-spdy.txt","java.version=1.7.0_21");
}
@Test
public void testWithSpdyBadNpnVersion() throws Exception
{
//StartLog.enableDebug();
assertUseCase("home","base.enable.spdy.bad.npn.version","assert-enable-spdy-bad-npn-version.txt","java.version=1.7.0_01");
}
@Test
public void testWithDatabase() throws Exception

View File

@ -0,0 +1,16 @@
# The XMLs we expect (order is important)
# The LIBs we expect (order is irrelevant)
# The Properties we expect (order is irrelevant)
PROP|jetty.port=9090
PROP|jetty.keystore=etc/keystore
PROP|jetty.keystore.password=friendly
PROP|jetty.keymanager.password=icecream
PROP|jetty.truststore=etc/keystore
PROP|jetty.truststore.password=sundae
PROP|java.version=1.7.0_01
# The Downloads
# The Bootlib

View File

@ -0,0 +1,28 @@
# The XMLs we expect (order is important)
XML|${jetty.home}/etc/jetty-jmx.xml
XML|${jetty.home}/etc/jetty.xml
XML|${jetty.home}/etc/jetty-http.xml
# The LIBs we expect (order is irrelevant)
LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
LIB|${jetty.home}/lib/jetty-http-TEST.jar
LIB|${jetty.home}/lib/jetty-io-TEST.jar
LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
LIB|${jetty.home}/lib/jetty-server-TEST.jar
LIB|${jetty.home}/lib/jetty-util-TEST.jar
LIB|${jetty.home}/lib/jetty-xml-TEST.jar
LIB|${jetty.home}/lib/servlet-api-3.1.jar
# The Properties we expect (order is irrelevant)
PROP|jetty.port=9090
PROP|jetty.keystore=etc/keystore
PROP|jetty.keystore.password=friendly
PROP|jetty.keymanager.password=icecream
PROP|jetty.truststore=etc/keystore
PROP|jetty.truststore.password=sundae
PROP|java.version=1.7.0_01
# The Downloads
# The Bootlib

View File

@ -0,0 +1,12 @@
--module=server,http,jmx,spdy
jetty.port=9090
# Some SSL keystore configuration
jetty.keystore=etc/keystore
jetty.keystore.password=friendly
jetty.keymanager.password=icecream
jetty.truststore=etc/keystore
jetty.truststore.password=sundae

View File

@ -0,0 +1,12 @@
--module=server,http,jmx
jetty.port=9090
# Some SSL keystore configuration
jetty.keystore=etc/keystore
jetty.keystore.password=friendly
jetty.keymanager.password=icecream
jetty.truststore=etc/keystore
jetty.truststore.password=sundae

View File

@ -0,0 +1,9 @@
[name]
npn-boot
[files]
http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar
[ini-template]
--exec
-Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.util;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

View File

0
jetty-util/src/test/resources/TestData/test/alphabet Executable file → Normal file
View File

0
jetty-util/src/test/resources/TestData/test/numbers Executable file → Normal file
View File

View File

View File

View File

View File

View File

@ -397,6 +397,9 @@ public class WebInfConfiguration extends AbstractConfiguration
web_app = context.newResource(war);
else
web_app=context.getBaseResource();
if (web_app == null)
throw new IllegalStateException("No resourceBase or war set for context");
// Accept aliases for WAR files
if (web_app.getAlias() != null)
@ -603,7 +606,7 @@ public class WebInfConfiguration extends AbstractConfiguration
if (resource == null)
{
if (context.getWar()==null || context.getWar().length()==0)
resource=context.newResource(context.getResourceBase());
throw new IllegalStateException("No resourceBase or war set for context");
// Set dir or WAR
resource = context.newResource(context.getWar());
@ -621,7 +624,8 @@ public class WebInfConfiguration extends AbstractConfiguration
}
catch (Exception e)
{
LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
LOG.warn("Can't generate resourceBase as part of webapp tmp dir name: " + e);
LOG.debug(e);
}
//Context name

View File

@ -30,7 +30,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.DeploymentException;
@ -187,6 +186,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
@Override
protected void doStop() throws Exception
{
ShutdownThread.deregister(this);
endpointClientMetadataCache.clear();
super.doStop();
}

View File

@ -18,9 +18,9 @@
package org.eclipse.jetty.websocket.jsr356;
import java.net.HttpCookie;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpointConfig.Configurator;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
@ -46,7 +46,23 @@ public class JsrUpgradeListener implements UpgradeListener
Map<String, List<String>> headers = request.getHeaders();
configurator.beforeRequest(headers);
request.setHeaders(headers);
// Handle cookies
for (String name : headers.keySet())
{
if ("cookie".equalsIgnoreCase(name))
{
List<String> values = headers.get(name);
if (values != null)
{
for (String cookie : values)
{
List<HttpCookie> cookies = HttpCookie.parse(cookie);
request.getCookies().addAll(cookies);
}
}
}
}
}
@Override

View File

@ -0,0 +1,179 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.websocket.jsr356;
import java.net.HttpCookie;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ContainerProvider;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
public class CookiesTest
{
private Server server;
private ServerConnector connector;
protected void startServer(Handler handler) throws Exception
{
server = new Server();
connector = new ServerConnector(server);
server.addConnector(connector);
ContextHandler context = new ContextHandler();
context.setContextPath("/");
context.setHandler(handler);
server.setHandler(context);
server.start();
}
@After
public void stopServer() throws Exception
{
server.stop();
}
@Test
public void testCookiesAreSentToServer() throws Exception
{
final String cookieName = "name";
final String cookieValue = "value";
final String cookieString = cookieName + "=" + cookieValue;
startServer(new EchoHandler()
{
@Override
public Object createWebSocket(ServletUpgradeRequest request, ServletUpgradeResponse response)
{
List<HttpCookie> cookies = request.getCookies();
Assert.assertNotNull(cookies);
Assert.assertEquals(1, cookies.size());
HttpCookie cookie = cookies.get(0);
Assert.assertEquals(cookieName, cookie.getName());
Assert.assertEquals(cookieValue, cookie.getValue());
Map<String, List<String>> headers = request.getHeaders();
// Test case insensitivity
Assert.assertTrue(headers.containsKey("cookie"));
List<String> values = headers.get("Cookie");
Assert.assertNotNull(values);
Assert.assertEquals(1, values.size());
Assert.assertEquals(cookieString, values.get(0));
return super.createWebSocket(request, response);
}
});
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
builder.configurator(new ClientEndpointConfig.Configurator()
{
@Override
public void beforeRequest(Map<String, List<String>> headers)
{
headers.put("Cookie", Collections.singletonList(cookieString));
}
});
ClientEndpointConfig config = builder.build();
Endpoint endPoint = new Endpoint()
{
@Override
public void onOpen(Session session, EndpointConfig config)
{
}
};
Session session = container.connectToServer(endPoint, config, URI.create("ws://localhost:" + connector.getLocalPort()));
session.close();
}
@Test
public void testCookiesAreSentToClient() throws Exception
{
final String cookieName = "name";
final String cookieValue = "value";
final String cookieDomain = "domain";
final String cookiePath = "/path";
startServer(new EchoHandler()
{
@Override
public Object createWebSocket(ServletUpgradeRequest request, ServletUpgradeResponse response)
{
String cookieString = cookieName + "=" + cookieValue + ";Domain=" + cookieDomain + ";Path=" + cookiePath;
response.getHeaders().put("Set-Cookie", Collections.singletonList(cookieString));
return super.createWebSocket(request, response);
}
});
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
builder.configurator(new ClientEndpointConfig.Configurator()
{
@Override
public void afterResponse(HandshakeResponse response)
{
Map<String, List<String>> headers = response.getHeaders();
// Test case insensitivity
Assert.assertTrue(headers.containsKey("set-cookie"));
List<String> values = headers.get("Set-Cookie");
Assert.assertNotNull(values);
Assert.assertEquals(1, values.size());
List<HttpCookie> cookies = HttpCookie.parse(values.get(0));
Assert.assertEquals(1, cookies.size());
HttpCookie cookie = cookies.get(0);
Assert.assertEquals(cookieName, cookie.getName());
Assert.assertEquals(cookieValue, cookie.getValue());
Assert.assertEquals(cookieDomain, cookie.getDomain());
Assert.assertEquals(cookiePath, cookie.getPath());
}
});
ClientEndpointConfig config = builder.build();
Endpoint endPoint = new Endpoint()
{
@Override
public void onOpen(Session session, EndpointConfig config)
{
}
};
Session session = container.connectToServer(endPoint, config, URI.create("ws://localhost:" + connector.getLocalPort()));
session.close();
}
}

View File

@ -26,11 +26,13 @@ import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.DateDecoder;
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.TimeEncoder;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.ConfiguredEchoSocket;
@ -38,6 +40,7 @@ import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoSocketConfigur
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
/**
@ -45,6 +48,9 @@ import org.junit.Test;
*/
public class AnnotatedServerEndpointTest
{
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private static WSServer server;
@BeforeClass
@ -72,7 +78,7 @@ public class AnnotatedServerEndpointTest
private void assertResponse(String message, String... expectedTexts) throws Exception
{
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.start();

View File

@ -23,10 +23,12 @@ import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpoint;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpointConfigContextListener;
import org.junit.Assert;
@ -42,6 +44,9 @@ public class BasicEndpointTest
@Rule
public TestingDir testdir = new TestingDir();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
@Test
public void testEcho() throws Exception
{
@ -61,7 +66,7 @@ public class BasicEndpointTest
wsb.deployWebapp(webapp);
// wsb.dump();
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.start();

View File

@ -29,6 +29,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.log.Log;
@ -36,6 +37,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.IdleTimeoutContextListener;
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutEndpoint;
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutSocket;
@ -51,6 +53,9 @@ public class IdleTimeoutTest
@Rule
public TestingDir testdir = new TestingDir();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private static WSServer server;
@BeforeClass
@ -80,7 +85,7 @@ public class IdleTimeoutTest
private void assertConnectionTimeout(URI uri) throws Exception, IOException, InterruptedException, ExecutionException, TimeoutException
{
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.start();

View File

@ -25,10 +25,12 @@ import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoConfiguredSocket;
import org.junit.Assert;
import org.junit.Rule;
@ -42,6 +44,9 @@ public class LargeAnnotatedTest
@Rule
public TestingDir testdir = new TestingDir();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
@Test
public void testEcho() throws Exception
{
@ -58,7 +63,7 @@ public class LargeAnnotatedTest
wsb.deployWebapp(webapp);
// wsb.dump();
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.getPolicy().setMaxTextMessageSize(128*1024);

View File

@ -25,10 +25,12 @@ import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoDefaultSocket;
import org.junit.Assert;
import org.junit.Rule;
@ -42,6 +44,9 @@ public class LargeContainerTest
@Rule
public TestingDir testdir = new TestingDir();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
@Test
public void testEcho() throws Exception
{
@ -58,7 +63,7 @@ public class LargeContainerTest
wsb.deployWebapp(webapp);
// wsb.dump();
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.getPolicy().setMaxTextMessageSize(128*1024);

View File

@ -23,10 +23,12 @@ import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoReturnEndpoint;
import org.junit.Assert;
import org.junit.Rule;
@ -37,6 +39,9 @@ public class OnMessageReturnTest
@Rule
public TestingDir testdir = new TestingDir();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
@Test
public void testEchoReturn() throws Exception
{
@ -53,7 +58,7 @@ public class OnMessageReturnTest
wsb.deployWebapp(webapp);
wsb.dump();
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.start();

View File

@ -27,15 +27,19 @@ import java.util.List;
import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@ -95,7 +99,11 @@ public class SessionTest
return cases;
}
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private final Case testcase;
private final static AtomicInteger ID = new AtomicInteger(0);
private WSServer server;
private URI serverUri;
@ -107,7 +115,7 @@ public class SessionTest
@Before
public void startServer() throws Exception
{
server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName()),"app");
server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName() + "-" + ID.incrementAndGet()),"app");
server.copyWebInf("empty-web.xml");
server.copyClass(SessionInfoSocket.class);
server.copyClass(SessionAltConfig.class);
@ -127,7 +135,7 @@ public class SessionTest
private void assertResponse(String requestPath, String requestMessage, String expectedResponse) throws Exception
{
WebSocketClient client = new WebSocketClient();
WebSocketClient client = new WebSocketClient(bufferPool);
try
{
client.start();

View File

@ -19,7 +19,6 @@
<module>websocket-client</module>
<module>websocket-server</module>
<module>websocket-servlet</module>
<module>websocket-mux-extension</module>
<module>javax-websocket-client-impl</module>
<module>javax-websocket-server-impl</module>
</modules>

View File

@ -262,7 +262,8 @@ public class UpgradeRequest
public void setCookies(List<HttpCookie> cookies)
{
this.cookies = cookies;
this.cookies.clear();
this.cookies.addAll(cookies);
}
public void setExtensions(List<ExtensionConfig> configs)

View File

@ -24,10 +24,10 @@ import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ThreadLocalRandom;
import org.eclipse.jetty.util.B64Code;
@ -44,15 +44,17 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
*/
public class ClientUpgradeRequest extends UpgradeRequest
{
private final static Logger LOG = Log.getLogger(ClientUpgradeRequest.class);
private final static int MAX_KEYS = -1; // maximum number of parameter keys to decode
private static final Logger LOG = Log.getLogger(ClientUpgradeRequest.class);
private static final int MAX_KEYS = -1; // maximum number of parameter keys to decode
private static final Set<String> FORBIDDEN_HEADERS;
static
{
// headers not allowed to be set in ClientUpgradeRequest.headers
FORBIDDEN_HEADERS = new HashSet<>();
// Headers not allowed to be set in ClientUpgradeRequest.headers.
FORBIDDEN_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
// Cookies are handled explicitly, avoid to add them twice.
FORBIDDEN_HEADERS.add("cookie");
// Headers that cannot be set by applications.
FORBIDDEN_HEADERS.add("upgrade");
FORBIDDEN_HEADERS.add("host");
FORBIDDEN_HEADERS.add("connection");
@ -176,7 +178,7 @@ public class ClientUpgradeRequest extends UpgradeRequest
{
if (FORBIDDEN_HEADERS.contains(key))
{
LOG.warn("Skipping forbidden header - {}",key);
LOG.debug("Skipping forbidden header - {}",key);
continue; // skip
}
request.append(key).append(": ");

View File

@ -94,6 +94,11 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
{
this(null,executor);
}
public WebSocketClient(ByteBufferPool bufferPool)
{
this(null,null,bufferPool);
}
public WebSocketClient(SslContextFactory sslContextFactory)
{
@ -101,11 +106,16 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
}
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor)
{
this(sslContextFactory,executor,new MappedByteBufferPool());
}
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool)
{
this.executor = executor;
this.sslContextFactory = sslContextFactory;
this.policy = WebSocketPolicy.newClientPolicy();
this.bufferPool = new MappedByteBufferPool();
this.bufferPool = bufferPool;
this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool);
this.masker = new RandomMasker();
this.eventDriverFactory = new EventDriverFactory(policy);
@ -254,7 +264,7 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
}
super.doStop();
LOG.info("Stopped {}",this);
LOG.debug("Stopped {}",this);
}
/**

View File

@ -22,10 +22,12 @@ import java.net.URI;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.common.test.BlockheadServer;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
import org.junit.After;
import org.junit.Before;
@ -41,13 +43,16 @@ public class BadNetworkTest
@Rule
public TestTracker tt = new TestTracker();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private BlockheadServer server;
private WebSocketClient client;
@Before
public void startClient() throws Exception
{
client = new WebSocketClient();
client = new WebSocketClient(bufferPool);
client.getPolicy().setIdleTimeout(250);
client.start();
}

View File

@ -33,12 +33,14 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeException;
import org.eclipse.jetty.websocket.common.AcceptHash;
import org.eclipse.jetty.websocket.common.test.BlockheadServer;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
import org.junit.After;
import org.junit.Assert;
@ -55,6 +57,9 @@ public class ClientConnectTest
@Rule
public TestTracker tt = new TestTracker();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private final int timeout = 500;
private BlockheadServer server;
private WebSocketClient client;
@ -86,7 +91,7 @@ public class ClientConnectTest
@Before
public void startClient() throws Exception
{
client = new WebSocketClient();
client = new WebSocketClient(bufferPool);
client.setConnectTimeout(timeout);
client.start();
}

View File

@ -29,11 +29,13 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
/**
* This is not a general purpose websocket client. It's only for testing the websocket server and is hardwired to a specific draft version of the protocol.
@ -95,6 +97,8 @@ public class TestClient
private static final Random __random = new Random();
private static LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("TestClient",new MappedByteBufferPool());
private final String _host;
private final int _port;
private final String _protocol;
@ -172,7 +176,7 @@ public class TestClient
}
TestClient[] client = new TestClient[clients];
WebSocketClient wsclient = new WebSocketClient();
WebSocketClient wsclient = new WebSocketClient(bufferPool);
try
{
wsclient.start();
@ -250,6 +254,7 @@ public class TestClient
wsclient.stop();
}
bufferPool.assertNoLeaks();
}
private static void usage(String[] args)

View File

@ -67,8 +67,7 @@ public class Parser
private int cursor = 0;
// Frame
private WebSocketFrame frame;
private Frame priorDataFrame;
private byte lastDataOpcode;
private boolean priorDataFrame;
// payload specific
private ByteBuffer payload;
private int payloadLength;
@ -186,7 +185,7 @@ public class Parser
{
if (LOG.isDebugEnabled())
{
LOG.debug("{} Notify {}",policy.getBehavior(),incomingFramesHandler);
LOG.debug("{} Notify {}",policy.getBehavior(),getIncomingFramesHandler());
}
if (policy.getBehavior() == WebSocketBehavior.SERVER)
@ -201,14 +200,15 @@ public class Parser
* But we can't trust the client at this point, so Jetty opts to close
* the connection as a Protocol error.
*/
if (f.isMasked() == false)
if (!f.isMasked())
{
throw new ProtocolException("Client MUST mask all frames (RFC-6455: Section 5.1)");
}
} else if(policy.getBehavior() == WebSocketBehavior.CLIENT)
}
else if(policy.getBehavior() == WebSocketBehavior.CLIENT)
{
// Required by RFC-6455 / Section 5.1
if (f.isMasked() == true)
if (f.isMasked())
{
throw new ProtocolException("Server MUST NOT mask any frames (RFC-6455: Section 5.1)");
}
@ -258,30 +258,36 @@ public class Parser
{
LOG.debug("{} Parsed Frame: {}",policy.getBehavior(),frame);
notifyFrame(frame);
if (frame.isDataFrame() && frame.isFin())
if (frame.isDataFrame())
{
priorDataFrame = null;
}
else
{
priorDataFrame = frame;
priorDataFrame = !frame.isFin();
}
reset();
}
}
catch (WebSocketException e)
{
buffer.position(buffer.limit()); // consume remaining
this.payload = null; // reset
reset();
notifyWebSocketException(e);
}
catch (Throwable t)
{
buffer.position(buffer.limit()); // consume remaining
this.payload = null; // reset
reset();
notifyWebSocketException(new WebSocketException(t));
}
}
private void reset()
{
if (frame != null)
frame.reset();
frame = null;
bufferPool.release(payload);
payload = null;
}
/**
* Parse the base framing protocol buffer.
* <p>
@ -295,11 +301,6 @@ public class Parser
*/
private boolean parseFrame(ByteBuffer buffer)
{
if (buffer.remaining() <= 0)
{
return false;
}
LOG.debug("{} Parsing {} bytes",policy.getBehavior(),buffer.remaining());
while (buffer.hasRemaining())
{
@ -307,21 +308,15 @@ public class Parser
{
case START:
{
if ((frame != null) && (frame.isFin()))
{
frame.reset();
}
// peek at byte
byte b = buffer.get();
boolean fin = ((b & 0x80) != 0);
byte opc = (byte)(b & 0x0F);
byte opcode = opc;
byte opcode = (byte)(b & 0x0F);
if (!OpCode.isKnown(opcode))
{
throw new ProtocolException("Unknown opcode: " + opc);
throw new ProtocolException("Unknown opcode: " + opcode);
}
if (LOG.isDebugEnabled())
@ -335,36 +330,32 @@ public class Parser
}
// base framing flags
switch(opcode)
switch(opcode)
{
case OpCode.TEXT:
frame = new TextFrame();
lastDataOpcode = opcode;
// data validation
if ((priorDataFrame != null) && (!priorDataFrame.isFin()))
if (priorDataFrame)
{
throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
}
break;
case OpCode.BINARY:
frame = new BinaryFrame();
lastDataOpcode = opcode;
// data validation
if ((priorDataFrame != null) && (!priorDataFrame.isFin()))
if (priorDataFrame)
{
throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
}
break;
case OpCode.CONTINUATION:
frame = new ContinuationFrame();
lastDataOpcode = opcode;
// continuation validation
if (priorDataFrame == null)
if (!priorDataFrame)
{
throw new ProtocolException("CONTINUATION frame without prior !FIN");
}
// Be careful to use the original opcode
opcode = lastDataOpcode;
break;
case OpCode.CLOSE:
frame = new CloseFrame();
@ -556,11 +547,13 @@ public class Parser
case PAYLOAD:
{
frame.assertValid();
if (parsePayload(buffer))
{
// special check for close
if (frame.getOpCode() == OpCode.CLOSE)
{
// TODO: yuck. Don't create an object to do validation checks!
new CloseInfo(frame);
}
state = State.START;
@ -591,21 +584,18 @@ public class Parser
if (buffer.hasRemaining())
{
if (payload == null)
{
frame.assertValid();
payload = bufferPool.acquire(payloadLength,false);
BufferUtil.clearToFill(payload);
}
// Create a small window of the incoming buffer to work with.
// this should only show the payload itself, and not any more
// bytes that could belong to the start of the next frame.
ByteBuffer window = buffer.slice();
int bytesExpected = payloadLength - payload.position();
int bytesSoFar = payload == null ? 0 : payload.position();
int bytesExpected = payloadLength - bytesSoFar;
int bytesAvailable = buffer.remaining();
int windowBytes = Math.min(bytesAvailable,bytesExpected);
window.limit(window.position() + windowBytes);
int windowBytes = Math.min(bytesAvailable, bytesExpected);
int limit = buffer.limit();
buffer.limit(buffer.position() + windowBytes);
ByteBuffer window = buffer.slice();
buffer.limit(limit);
buffer.position(buffer.position() + window.remaining());
if (LOG.isDebugEnabled())
{
@ -613,17 +603,30 @@ public class Parser
}
maskProcessor.process(window);
int len = BufferUtil.put(window,payload);
buffer.position(buffer.position() + len); // update incoming buffer position
if (payload.position() >= payloadLength)
if (window.remaining() == payloadLength)
{
BufferUtil.flipToFlush(payload,0);
frame.setPayload(payload);
this.payload = null;
// We have the whole content, no need to copy.
frame.setPayload(window);
return true;
}
else
{
if (payload == null)
{
payload = bufferPool.acquire(payloadLength,false);
BufferUtil.clearToFill(payload);
}
// Copy the payload.
payload.put(window);
if (payload.position() == payloadLength)
{
BufferUtil.flipToFlush(payload, 0);
frame.setPayload(payload);
return true;
}
}
}
return false;
}

View File

@ -56,37 +56,42 @@ import org.eclipse.jetty.websocket.common.frames.TextFrame;
*/
public abstract class WebSocketFrame implements Frame
{
public static WebSocketFrame copy(Frame copy)
public static WebSocketFrame copy(Frame original)
{
WebSocketFrame frame = null;
switch (copy.getOpCode())
WebSocketFrame copy;
switch (original.getOpCode())
{
case OpCode.BINARY:
frame = new BinaryFrame();
copy = new BinaryFrame();
break;
case OpCode.TEXT:
frame = new TextFrame();
copy = new TextFrame();
break;
case OpCode.CLOSE:
frame = new CloseFrame();
copy = new CloseFrame();
break;
case OpCode.CONTINUATION:
frame = new ContinuationFrame();
copy = new ContinuationFrame();
break;
case OpCode.PING:
frame = new PingFrame();
copy = new PingFrame();
break;
case OpCode.PONG:
frame = new PongFrame();
copy = new PongFrame();
break;
default:
throw new IllegalArgumentException("Cannot copy frame with opcode " + copy.getOpCode() + " - " + copy);
throw new IllegalArgumentException("Cannot copy frame with opcode " + original.getOpCode() + " - " + original);
}
frame.copyHeaders(copy);
frame.setPayload(copy.getPayload());
return frame;
copy.copyHeaders(original);
ByteBuffer payload = original.getPayload();
if (payload != null)
{
ByteBuffer payloadCopy = ByteBuffer.allocate(payload.remaining());
payloadCopy.put(payload.slice()).flip();
copy.setPayload(payloadCopy);
}
return copy;
}
/**
@ -123,12 +128,6 @@ public abstract class WebSocketFrame implements Frame
public abstract void assertValid();
protected void copy(WebSocketFrame copy, ByteBuffer payload)
{
copyHeaders(copy);
setPayload(payload);
}
protected void copyHeaders(Frame frame)
{
finRsvOp = 0x00;
@ -348,13 +347,7 @@ public abstract class WebSocketFrame implements Frame
*/
public WebSocketFrame setPayload(ByteBuffer buf)
{
if (buf == null)
{
data = null;
return this;
}
data = buf.slice();
data = buf;
return this;
}

View File

@ -122,15 +122,9 @@ public abstract class ControlFrame extends WebSocketFrame
@Override
public WebSocketFrame setPayload(ByteBuffer buf)
{
if (buf == null)
if (buf != null && buf.remaining() > MAX_CONTROL_PAYLOAD)
{
data = null;
return this;
}
if (buf.remaining() > ControlFrame.MAX_CONTROL_PAYLOAD)
{
throw new ProtocolException("Control Payloads can not exceed 125 bytes in length.");
throw new ProtocolException("Control Payloads can not exceed " + MAX_CONTROL_PAYLOAD + " bytes in length.");
}
return super.setPayload(buf);
}

View File

@ -78,13 +78,20 @@ public class DataFrame extends WebSocketFrame
return true;
}
public void releaseBuffer()
public void reset()
{
// TODO: this is rather ugly.
// The ByteBufferPool is set only from extensions that
// compress the payload. It would be better to wrap the
// callback associated with this DataFrame into one that
// releases the buffer and then call the nested callback,
// rather than null-checking whether the pool exists and
// if so then release the buffer.
if (pool!=null)
{
pool.release(this.data);
this.data=null;
}
super.reset();
}
/**

View File

@ -358,16 +358,9 @@ public class FrameFlusher
headerBuffer = null;
}
if (!frame.hasPayload())
{
return;
}
if (frame instanceof DataFrame)
{
// TODO null payload within frame
DataFrame data = (DataFrame)frame;
data.releaseBuffer();
((DataFrame)frame).reset();
}
}

View File

@ -41,25 +41,19 @@ public class MessageInputStream extends InputStream implements MessageAppender
private static final Logger LOG = Log.getLogger(MessageInputStream.class);
// EOF (End of Buffers)
private final static ByteBuffer EOF = ByteBuffer.allocate(0).asReadOnlyBuffer();
/**
* Used for controlling read suspend/resume behavior if the queue is full, but the read operations haven't caught up yet.
*/
@SuppressWarnings("unused")
private final LogicalConnection connection;
private final BlockingDeque<ByteBuffer> buffers = new LinkedBlockingDeque<>();
private AtomicBoolean closed = new AtomicBoolean(false);
private final long timeoutMs;
private ByteBuffer activeBuffer = null;
private long timeoutMs = -1;
public MessageInputStream(LogicalConnection connection)
{
this.connection = connection;
this.timeoutMs = -1; // disabled
this(connection, -1);
}
public MessageInputStream(LogicalConnection connection, int timeoutMs)
{
this.connection = connection;
this.timeoutMs = timeoutMs;
}
@ -71,16 +65,22 @@ public class MessageInputStream extends InputStream implements MessageAppender
LOG.debug("appendMessage(ByteBuffer,{}): {}",fin,BufferUtil.toDetailString(framePayload));
}
// if closed, we should just toss incoming payloads into the bit bucket.
// If closed, we should just toss incoming payloads into the bit bucket.
if (closed.get())
{
return;
}
// Put the payload into the queue
// Put the payload into the queue, by copying it.
// Copying is necessary because the payload will
// be processed after this method returns.
try
{
buffers.put(framePayload);
int capacity = framePayload.remaining();
// TODO: the copy buffer should be pooled too, but no buffer pool available from here.
ByteBuffer copy = framePayload.isDirect() ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity);
copy.put(framePayload).flip();
buffers.put(copy);
}
catch (InterruptedException e)
{
@ -141,7 +141,7 @@ public class MessageInputStream extends InputStream implements MessageAppender
// grab a fresh buffer
while (activeBuffer == null || !activeBuffer.hasRemaining())
{
if (timeoutMs == -1)
if (timeoutMs < 0)
{
// infinite take
activeBuffer = buffers.take();

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