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> <parent>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId> <artifactId>jetty-project</artifactId>
<version>9.1.0-SNAPSHOT</version> <version>9.1.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -25,7 +25,7 @@
<configuration> <configuration>
<excludes>**/MANIFEST.MF,javax/**</excludes> <excludes>**/MANIFEST.MF,javax/**</excludes>
<excludeArtifactIds>javax</excludeArtifactIds> <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> <outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases> <overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots> <overWriteSnapshots>true</overWriteSnapshots>
@ -43,7 +43,7 @@
<excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes> <excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes>
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds> <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
<excludeArtifactIds>javax</excludeArtifactIds> <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> <outputDirectory>${project.build.directory}/sources</outputDirectory>
<overWriteReleases>true</overWriteReleases> <overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots> <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.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; 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.HttpHeader;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme; 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); HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient(); HttpClient client = destination.getHttpClient();
ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory); 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); org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
Helper.replaceConnection(oldConnection, newConnection); 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); LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
} }
catch (Throwable x) catch (Throwable x)

View File

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

View File

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

View File

@ -48,6 +48,7 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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.ContentProvider;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination; 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.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesContentProvider; 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.HttpField;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.toolchain.test.annotation.Slow; 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.IO;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert; import org.junit.Assert;
@ -1073,4 +1078,86 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Assert.assertEquals(200, response.getStatus()); 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> </goals>
<configuration> <configuration>
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds> <includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
<includeArtifactIds>javax.activation,javax.mail.glassfish</includeArtifactIds> <includeArtifactIds>javax.mail.glassfish</includeArtifactIds>
<includeTypes>jar</includeTypes> <includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jndi</outputDirectory> <outputDirectory>${assembly-directory}/lib/jndi</outputDirectory>
</configuration> </configuration>
@ -440,7 +440,34 @@
</goals> </goals>
<configuration> <configuration>
<includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain</includeGroupIds> <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> <includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory> <outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration> </configuration>
@ -551,29 +578,27 @@
</build> </build>
<dependencies> <dependencies>
<!-- Orbit Deps --> <!-- 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> <dependency>
<groupId>org.eclipse.jetty.orbit</groupId> <groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.mail.glassfish</artifactId> <artifactId>javax.mail.glassfish</artifactId>
</dependency> </dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.orbit</groupId> <groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.security.auth.message</artifactId> <artifactId>javax.security.auth.message</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.orbit</groupId> <groupId>javax.annotation</groupId>
<artifactId>org.apache.taglibs.standard.glassfish</artifactId> <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>
<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; _persistent = null;
_contentPrepared = 0; _contentPrepared = 0;
_needCRLF = false; _needCRLF = false;
_noContent=false;
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -744,13 +743,14 @@ public class HttpGenerator
} }
else else
{ {
// No idea, so we must assume that a body is coming // 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; _endOfContent = EndOfContent.CHUNKED_CONTENT;
if (response!=null && _endOfContent==EndOfContent.EOF_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
_endOfContent=EndOfContent.NO_CONTENT; // we *must* close the connection, otherwise the client
_noContent=true; // 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; break;
@ -825,7 +825,7 @@ public class HttpGenerator
header.put(CONNECTION_KEEP_ALIVE); header.put(CONNECTION_KEEP_ALIVE);
else 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((byte)',');
header.put(StringUtil.getBytes(connection.toString())); header.put(StringUtil.getBytes(connection.toString()));
header.put(CRLF); header.put(CRLF);
@ -833,7 +833,7 @@ public class HttpGenerator
} }
else if (connection!=null) else if (connection!=null)
{ {
header.put(CONNECTION_); header.put(HttpHeader.CONNECTION.getBytesColonSpace());
header.put(StringUtil.getBytes(connection.toString())); header.put(StringUtil.getBytes(connection.toString()));
header.put(CRLF); 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[] 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_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_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[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
private static final byte[] CRLF = StringUtil.getBytes("\015\012"); private static final byte[] CRLF = StringUtil.getBytes("\015\012");
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\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,298 +18,21 @@
package org.eclipse.jetty.http; 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.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo; import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.junit.Assert;
import org.junit.Test; 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 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 @Test
public void testSendServerXPoweredBy() throws Exception public void testSendServerXPoweredBy() throws Exception
{ {
@ -365,8 +88,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator(); HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
@ -399,8 +121,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator(); HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
@ -495,9 +216,7 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. "); ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator(); HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
@ -540,15 +259,12 @@ public class HttpGeneratorServerTest
@Test @Test
public void test100ThenResponseWithContent() throws Exception public void test100ThenResponseWithContent() throws Exception
{ {
ByteBuffer header = BufferUtil.allocate(4096); ByteBuffer header = BufferUtil.allocate(4096);
ByteBuffer content0 = BufferUtil.toBuffer("Hello World! "); ByteBuffer content0 = BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. "); ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator(); HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result HttpGenerator.Result result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result); assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
@ -563,7 +279,6 @@ public class HttpGeneratorServerTest
assertThat(out, containsString("HTTP/1.1 100 Continue")); assertThat(out, containsString("HTTP/1.1 100 Continue"));
result = gen.generateResponse(null, null, null, content0, false); result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
@ -603,4 +318,22 @@ public class HttpGeneratorServerTest
assertThat(out, containsString("Content-Length: 59")); assertThat(out, containsString("Content-Length: 59"));
assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. ")); 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); LeakTrackingByteBufferPool.this.leaked(leakInfo);
} }
}; };
private final ByteBufferPool delegate; private final ByteBufferPool delegate;
public LeakTrackingByteBufferPool(ByteBufferPool delegate) public LeakTrackingByteBufferPool(ByteBufferPool delegate)
@ -51,20 +52,22 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
{ {
ByteBuffer buffer = delegate.acquire(size, direct); ByteBuffer buffer = delegate.acquire(size, direct);
if (!leakDetector.acquired(buffer)) if (!leakDetector.acquired(buffer))
LOG.info("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer)); LOG.warn("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
return buffer; return buffer;
} }
@Override @Override
public void release(ByteBuffer buffer) public void release(ByteBuffer buffer)
{ {
if (buffer == null)
return;
if (!leakDetector.released(buffer)) 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); 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> </dependency>
<!-- JSTL Impl --> <!-- JSTL Impl -->
<dependency> <dependency>
<groupId>org.eclipse.jetty.orbit</groupId> <groupId>org.glassfish.web</groupId>
<artifactId>org.apache.taglibs.standard.glassfish</artifactId> <artifactId>javax.servlet.jsp.jstl</artifactId>
</dependency> </dependency>
<!-- EL Api --> <!-- EL Api -->

View File

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

View File

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

View File

@ -7,7 +7,7 @@ server
client client
[lib] [lib]
lib/jetty-monitor-${jetty.version}.jar lib/monitor/jetty-monitor-${jetty.version}.jar
[xml] [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;version="[2.3,2.4)",
javax.servlet.jsp.el;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.tagext;version="[2.3,2.4)",
javax.servlet.jsp.jstl.core;version="1.2.0";resolution:=optional, javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional,
javax.servlet.jsp.jstl.fmt;version="1.2.0";resolution:=optional, javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
javax.servlet.jsp.jstl.sql;version="1.2.0";resolution:=optional, javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
javax.servlet.jsp.jstl.tlv;version="1.2.0";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;version="[2.3.2,2.4)";resolution:=optional,
org.apache.jasper.compiler;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, 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.util;version="[2.3.2,2.4)";resolution:=optional,
org.apache.jasper.xmlparser;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.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;version="1.2";resolution:=optional,
org.apache.taglibs.standard.extra.spath;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
org.apache.taglibs.standard.functions;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.parser;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.jstl.test;version="1.2.0";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.0";resolution:=optional, org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional,
org.apache.taglibs.standard.lang.support;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional,
org.apache.taglibs.standard.resources;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.resources;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.core;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.fmt;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.sql;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.common.xml;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.core;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.fmt;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.sql;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.el.xml;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.core;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.fmt;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.sql;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tag.rt.xml;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tei;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tlv;version="1.2.0";resolution:=optional, org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
org.osgi.*, org.osgi.*,
org.xml.*;resolution:=optional, org.xml.*;resolution:=optional,
org.xml.sax.*;resolution:=optional, org.xml.sax.*;resolution:=optional,

View File

@ -25,6 +25,8 @@ import org.eclipse.jetty.annotations.AnnotationParser.Handler;
import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants; import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker; 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.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle; import org.osgi.framework.Bundle;
@ -37,6 +39,8 @@ import org.osgi.framework.Constants;
*/ */
public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration 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 class BundleParserTask extends ParserTask
{ {
@ -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; org.eclipse.jetty.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.osgi.annotations.AnnotationParser)_parser;
Bundle bundle = osgiAnnotationParser.getBundle(_resource); Bundle bundle = osgiAnnotationParser.getBundle(_resource);
if (_stat != null)
_stat.start();
osgiAnnotationParser.parse(_handlers, bundle, _resolver); osgiAnnotationParser.parse(_handlers, bundle, _resolver);
if (_stat != null)
_stat.end();
} }
return null; return null;
} }
@ -178,7 +186,12 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
ClassNameResolver classNameResolver = createClassNameResolver(context); ClassNameResolver classNameResolver = createClassNameResolver(context);
if (_parserTasks != null) 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.add(CoreOptions.junitBundles());
options.addAll(configureJettyHomeAndPort("jetty-selector.xml")); options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.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()); options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
String logLevel = "WARN"; String logLevel = "WARN";
@ -123,7 +127,7 @@ public class TestJettyOSGiBootWebAppAsService
//jsp bundles //jsp bundles
res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject()); 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("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.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.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart()); 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.Option;
import org.ops4j.pax.exam.junit.Configuration; import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner; import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
/** /**
@ -70,6 +71,10 @@ public class TestJettyOSGiBootWithJsp
options.add(CoreOptions.junitBundles()); options.add(CoreOptions.junitBundles());
options.addAll(configureJettyHomeAndPort("jetty-selector.xml")); options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.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()); options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
String logLevel = "WARN"; String logLevel = "WARN";
@ -139,7 +144,7 @@ public class TestJettyOSGiBootWithJsp
//jetty jsp bundles //jetty jsp bundles
res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject()); 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("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.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.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart()); res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart());
@ -167,6 +172,7 @@ public class TestJettyOSGiBootWithJsp
TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT); TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
} }
@Test @Test
public void testJspDump() throws Exception public void testJspDump() throws Exception
{ {

View File

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

View File

@ -21,13 +21,25 @@ lib/jetty-io-${jetty.version}.jar
etc/jetty.xml etc/jetty.xml
[ini-template] [ini-template]
##
## Server Threading Configuration ## Server Threading Configuration
##
# minimum number of threads # minimum number of threads
threads.min=10 threads.min=10
# maximum number of threads # maximum number of threads
threads.max=200 threads.max=200
# thread idle timeout in milliseconds # thread idle timeout in milliseconds
threads.timeout=60000 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) # What host to listen on (leave commented to listen on all interfaces)
#jetty.host=myhost.com #jetty.host=myhost.com
# Dump the state of the Jetty server, components, and webapps after startup # 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) for (ConnectionFactory factory:factories)
addConnectionFactory(factory); addConnectionFactory(factory);
int cores = Runtime.getRuntime().availableProcessors();
if (acceptors < 0) if (acceptors < 0)
acceptors=Math.max(1,(Runtime.getRuntime().availableProcessors()) / 2); acceptors = 1 + cores / 16;
if (acceptors > 2 * Runtime.getRuntime().availableProcessors()) if (acceptors > 2 * cores)
LOG.warn("Acceptors should be <= 2*availableProcessors: " + this); LOG.warn("Acceptors should be <= 2*availableProcessors: " + this);
_acceptors = new Thread[acceptors]; _acceptors = new Thread[acceptors];
} }

View File

@ -19,7 +19,7 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import javax.servlet.ReadListener; import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream; import javax.servlet.ServletInputStream;
@ -29,38 +29,25 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
/** /**
* <p>{@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.</p> * {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.
* <p>{@link HttpInput} holds a queue of items passed to it by calls to {@link #content(T)}.</p> * <p/>
* <p>{@link HttpInput} stores the items directly; if the items contain byte buffers, it does not copy them * Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class
* but simply holds references to the item, thus the caller must organize for those buffers to valid while * maintains two states: the content state that tells whether there is content to consume and the EOF
* held by this class.</p> * state that tells whether an EOF has arrived.
* <p>To assist the caller, subclasses may override methods {@link #onContentQueued(T)}, * Only once the content has been consumed the content state is moved to the EOF state.
* {@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>
*/ */
public abstract class HttpInput<T> extends ServletInputStream implements Runnable public abstract class HttpInput<T> extends ServletInputStream implements Runnable
{ {
private final static Logger LOG = Log.getLogger(HttpInput.class); private final static Logger LOG = Log.getLogger(HttpInput.class);
private final byte[] _oneByteBuffer = new byte[1]; 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 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; private long _contentRead;
protected HttpInput() protected HttpInput()
@ -73,6 +60,14 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
_lock = lock == null ? this : lock; _lock = lock == null ? this : lock;
} }
public void init(HttpChannelState state)
{
synchronized (lock())
{
_channelState = state;
}
}
public final Object lock() public final Object lock()
{ {
return _lock; return _lock;
@ -82,50 +77,15 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{ {
synchronized (lock()) synchronized (lock())
{ {
_state = BLOCKING; _listener = null;
_eof=null;
_onError = null; _onError = null;
_notReady = false;
_contentState = STREAM;
_eofState = null;
_contentRead = 0; _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 @Override
public int available() public int available()
{ {
@ -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 @Override
public int read(byte[] b, int off, int len) throws IOException public int read(byte[] b, int off, int len) throws IOException
{ {
T item = null;
int l;
synchronized (lock()) synchronized (lock())
{ {
// System.err.printf("read s=%s q=%d e=%s%n",_state,_inputQ.size(),_eof); T item = getNextContent();
// Get the current head of the input Q
item = getNextContent();
// If we have no item
if (item == null) if (item == null)
{ {
_state.waitForContent(this); _contentState.waitForContent(this);
item = getNextContent(); item = getNextContent();
if (item == null) if (item == null)
return _state.noContent(); return _contentState.noContent();
} }
int l = get(item, b, off, len);
l=get(item, b, off, len);
_contentRead += l; _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); 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); 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); 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; 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() protected boolean onAsyncRead()
{
synchronized (lock())
{ {
if (_listener == null) if (_listener == null)
return false; return false;
}
_channelState.onReadPossible(); _channelState.onReadPossible();
return true; return true;
} }
@ -195,14 +224,10 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
} }
} }
/** Add some content to the input stream /**
* @param item * This method should be called to signal that an EOF has been
*/ * detected before all the expected content arrived.
public abstract void content(T item); * <p/>
/** This method should be called to signal to the HttpInput
* that an EOF has arrived before all the expected content.
* Typically this will result in an EOFException being thrown * Typically this will result in an EOFException being thrown
* from a subsequent read rather than a -1 return. * from a subsequent read rather than a -1 return.
*/ */
@ -210,29 +235,35 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{ {
synchronized (lock()) synchronized (lock())
{ {
if (_eof==null || !_eof.isEOF()) if (!isEOF())
{ {
LOG.debug("{} early EOF", this); LOG.debug("{} early EOF", this);
_eof=EARLY_EOF; _eofState = EARLY_EOF;
if (_listener!=null) if (_listener == null)
return;
}
}
_channelState.onReadPossible(); _channelState.onReadPossible();
} }
}
}
/**
* This method should be called to signal that all the expected
* content arrived.
*/
public void messageComplete() public void messageComplete()
{ {
synchronized (lock()) synchronized (lock())
{ {
if (_eof==null || !_eof.isEOF()) if (!isEOF())
{ {
LOG.debug("{} EOF", this); LOG.debug("{} EOF", this);
_eof=EOF; _eofState = EOF;
if (_listener!=null) if (_listener == null)
return;
}
}
_channelState.onReadPossible(); _channelState.onReadPossible();
} }
}
}
public void consumeAll() public void consumeAll()
{ {
@ -244,7 +275,7 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{ {
T item = getNextContent(); T item = getNextContent();
if (item == null) if (item == null)
_state.waitForContent(this); _contentState.waitForContent(this);
else else
consume(item, remaining(item)); consume(item, remaining(item));
} }
@ -256,36 +287,49 @@ 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 @Override
public boolean isFinished() public boolean isFinished()
{ {
synchronized (lock()) synchronized (lock())
{ {
return _state.isEOF(); return _contentState.isEOF();
} }
} }
@Override @Override
public boolean isReady() public boolean isReady()
{ {
boolean finished;
synchronized (lock()) synchronized (lock())
{ {
if (_contentState.isEOF())
return true;
if (_listener == null ) if (_listener == null )
return true; return true;
int available = available(); if (available() > 0)
if (available>0)
return true; return true;
if (!_notReady) if (_notReady)
{ return false;
_notReady = true; _notReady = true;
if (_state.isEOF()) finished = isFinished();
}
if (finished)
_channelState.onReadPossible(); _channelState.onReadPossible();
else else
unready(); unready();
}
return false; return false;
} }
}
protected void unready() protected void unready()
{ {
@ -294,18 +338,16 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
@Override @Override
public void setReadListener(ReadListener readListener) public void setReadListener(ReadListener readListener)
{ {
if (readListener==null) readListener = Objects.requireNonNull(readListener);
throw new NullPointerException("readListener==null");
synchronized (lock()) synchronized (lock())
{ {
if (_state!=BLOCKING) if (_contentState != STREAM)
throw new IllegalStateException("state="+_state); throw new IllegalStateException("state=" + _contentState);
_state=ASYNC; _contentState = ASYNC;
_listener = readListener; _listener = readListener;
_notReady = true; _notReady = true;
_channelState.onReadPossible();
} }
_channelState.onReadPossible();
} }
public void failed(Throwable x) public void failed(Throwable x)
@ -322,40 +364,41 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
@Override @Override
public void run() public void run()
{ {
final boolean available; final Throwable error;
final ReadListener listener;
boolean available = false;
final boolean eof; final boolean eof;
final Throwable x;
synchronized (lock()) synchronized (lock())
{ {
if (!_notReady || _listener == null) if (!_notReady || _listener == null)
return; return;
x=_onError; error = _onError;
T item; listener = _listener;
try try
{ {
item = getNextContent(); T item = getNextContent();
available = item != null && remaining(item) > 0;
} }
catch (Exception e) catch (Exception e)
{ {
item=null;
failed(e); failed(e);
} }
available= item!=null && remaining(item)>0;
eof = !available && _state.isEOF(); eof = !available && isFinished();
_notReady = !available && !eof; _notReady = !available && !eof;
} }
try try
{ {
if (x!=null) if (error != null)
_listener.onError(x); listener.onError(error);
else if (available) else if (available)
_listener.onDataAvailable(); listener.onDataAvailable();
else if (eof) else if (eof)
_listener.onAllDataRead(); listener.onAllDataRead();
else else
unready(); unready();
} }
@ -363,11 +406,11 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{ {
LOG.warn(e.toString()); LOG.warn(e.toString());
LOG.debug(e); 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 public void waitForContent(HttpInput<?> in) throws IOException
{ {
@ -384,16 +427,17 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
} }
} }
protected static final State BLOCKING= new State() protected static final State STREAM = new State()
{ {
@Override @Override
public void waitForContent(HttpInput<?> in) throws IOException public void waitForContent(HttpInput<?> input) throws IOException
{ {
in.blockForContent(); input.blockForContent();
} }
public String toString() public String toString()
{ {
return "OPEN"; return "STREAM";
} }
}; };
@ -404,6 +448,7 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{ {
return 0; return 0;
} }
@Override @Override
public String toString() public String toString()
{ {
@ -418,11 +463,13 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
{ {
throw new EofException(); throw new EofException();
} }
@Override @Override
public boolean isEOF() public boolean isEOF()
{ {
return true; return true;
} }
public String toString() public String toString()
{ {
return "EARLY_EOF"; return "EARLY_EOF";
@ -442,13 +489,4 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
return "EOF"; 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; private volatile Throwable _onError;
/* /*
ACTION OPEN ASYNC READY PENDING UNREADY ACTION OPEN ASYNC READY PENDING UNREADY CLOSED
------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------
setWriteListener() READY->owp ise ise ise ise setWriteListener() READY->owp ise ise ise ise ise
write() OPEN ise PENDING wpe wpe write() OPEN ise PENDING wpe wpe eof
flush() OPEN ise PENDING wpe wpe flush() OPEN ise PENDING wpe wpe eof
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false close() CLOSED CLOSED CLOSED CLOSED wpe CLOSED
write completed - - - ASYNC READY->owp 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 } enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, CLOSED }
private final AtomicReference<OutputState> _state=new AtomicReference<>(OutputState.OPEN); private final AtomicReference<OutputState> _state=new AtomicReference<>(OutputState.OPEN);
@ -131,9 +133,18 @@ public class HttpOutput extends ServletOutputStream implements Runnable
@Override @Override
public void close() public void close()
{ {
OutputState state=_state.get(); loop: while(true)
while(state!=OutputState.CLOSED)
{ {
OutputState state=_state.get();
switch (state)
{
case CLOSED:
break loop;
case UNREADY:
throw new WritePendingException(); // TODO ?
default:
if (_state.compareAndSet(state,OutputState.CLOSED)) if (_state.compareAndSet(state,OutputState.CLOSED))
{ {
try try
@ -148,16 +159,25 @@ public class HttpOutput extends ServletOutputStream implements Runnable
releaseBuffer(); releaseBuffer();
return; return;
} }
state=_state.get(); }
} }
} }
/* Called to indicated that the output is already closed and the state needs to be updated to match */ /* Called to indicated that the output is already closed and the state needs to be updated to match */
void closed() void closed()
{ {
OutputState state=_state.get(); loop: while(true)
while(state!=OutputState.CLOSED)
{ {
OutputState state=_state.get();
switch (state)
{
case CLOSED:
break loop;
case UNREADY:
throw new WritePendingException(); // TODO ?
default:
if (_state.compareAndSet(state,OutputState.CLOSED)) if (_state.compareAndSet(state,OutputState.CLOSED))
{ {
try try
@ -172,7 +192,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
releaseBuffer(); releaseBuffer();
return; return;
} }
state=_state.get(); }
} }
} }
@ -667,8 +687,9 @@ public class HttpOutput extends ServletOutputStream implements Runnable
return false; return false;
case UNREADY: case UNREADY:
return false; return false;
case CLOSED: case CLOSED:
return false; return true;
} }
} }
} }
@ -680,11 +701,13 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{ {
Throwable th=_onError; Throwable th=_onError;
_onError=null; _onError=null;
_writeListener.onError(th); _writeListener.onError(new IOException(th));
close(); close();
} }
if (_state.get()==OutputState.READY)
switch(_state.get())
{ {
case READY:
try try
{ {
_writeListener.onWritePossible(); _writeListener.onWritePossible();
@ -694,9 +717,33 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_writeListener.onError(e); _writeListener.onError(e);
close(); 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 private abstract class AsyncICB extends IteratingCallback
{ {
@ -722,7 +769,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
break; break;
case CLOSED: case CLOSED:
_onError=new EofException("Closed");
break; break;
default: 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,21 +21,19 @@ package org.eclipse.jetty.server;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import javax.servlet.ServletInputStream;
import org.eclipse.jetty.util.ArrayQueue; import org.eclipse.jetty.util.ArrayQueue;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
/** /**
* <p>{@link QueuedHttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.</p> * {@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.
* <p>{@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.</p> * <p/>
* <p>{@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them * {@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 * but simply holds references to the item, thus the caller must organize for those buffers to valid while
* held by this class.</p> * held by this class.
* <p>To assist the caller, subclasses may override methods {@link #onAsyncRead()}, * <p/>
* {@link #onContentConsumed(Object)} and {@link #onAllContentConsumed()} that can be implemented so that the * To assist the caller, subclasses may override methods {@link #onAsyncRead()}, {@link #onContentConsumed(Object)}
* caller will know when buffers are queued and consumed.</p> * that can be implemented so that the caller will know when buffers are queued and consumed.
*/ */
public abstract class QueuedHttpInput<T> extends HttpInput<T> public abstract class QueuedHttpInput<T> extends HttpInput<T>
{ {
@ -44,21 +42,37 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
private final ArrayQueue<T> _inputQ = new ArrayQueue<>(lock()); private final ArrayQueue<T> _inputQ = new ArrayQueue<>(lock());
public QueuedHttpInput() 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() public void recycle()
{ {
synchronized (lock()) synchronized (lock())
{ {
T item = _inputQ.peekUnsafe(); T item = _inputQ.pollUnsafe();
while (item != null) while (item != null)
{ {
_inputQ.pollUnsafe();
onContentConsumed(item); onContentConsumed(item);
item = _inputQ.pollUnsafe();
item = _inputQ.peekUnsafe();
if (item == null)
onAllContentConsumed();
} }
super.recycle(); super.recycle();
} }
@ -67,30 +81,27 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
@Override @Override
protected T nextContent() protected T nextContent()
{ {
synchronized (lock())
{
// Items are removed only when they are fully consumed.
T item = _inputQ.peekUnsafe(); T item = _inputQ.peekUnsafe();
// Skip consumed items at the head of the queue.
// Skip empty items at the head of the queue
while (item != null && remaining(item) == 0) while (item != null && remaining(item) == 0)
{ {
_inputQ.pollUnsafe(); _inputQ.pollUnsafe();
onContentConsumed(item); onContentConsumed(item);
LOG.debug("{} consumed {}", this, item); LOG.debug("{} consumed {}", this, item);
item = _inputQ.peekUnsafe(); item = _inputQ.peekUnsafe();
// If that was the last item then notify
if (item==null)
onAllContentConsumed();
} }
return item; return item;
} }
}
protected abstract void onContentConsumed(T item);
protected void blockForContent() throws IOException protected void blockForContent() throws IOException
{ {
synchronized (lock()) synchronized (lock())
{ {
while (_inputQ.isEmpty() && !_state.isEOF()) while (_inputQ.isEmpty() && !isFinished() && !isEOF())
{ {
try try
{ {
@ -105,40 +116,12 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
} }
} }
/**
/* ------------------------------------------------------------ */ * Callback that signals that the given content has been consumed.
/** Called by this HttpInput to signal all available content has been consumed *
* @param item the consumed content
*/ */
protected void onAllContentConsumed() protected abstract void onContentConsumed(T item);
{
}
/* ------------------------------------------------------------ */
/** 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);
}
}
public void earlyEOF() public void earlyEOF()
{ {
@ -157,5 +140,4 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
lock().notify(); lock().notify();
} }
} }
} }

View File

@ -208,7 +208,7 @@ public class ServerConnector extends AbstractNetworkConnector
@Name("factories") ConnectionFactory... factories) @Name("factories") ConnectionFactory... factories)
{ {
super(server,executor,scheduler,bufferPool,acceptors,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); addBean(_manager, true);
} }

View File

@ -21,6 +21,8 @@ package org.eclipse.jetty.server.handler;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
@ -2261,9 +2263,44 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
@Override @Override
public ClassLoader getClassLoader() 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")); AccessController.checkPermission(new RuntimePermission("getClassLoader"));
return _classLoader; return _classLoader;
} }
}
@Override @Override
public JspConfigDescriptor getJspConfigDescriptor() public JspConfigDescriptor getJspConfigDescriptor()

View File

@ -18,43 +18,29 @@
package org.eclipse.jetty.server.nio; 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 java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool; 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.ConnectionFactory;
import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NetworkTrafficServerConnector;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.Scheduler;
/** /**
* <p>A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.</p> * @deprecated use {@link org.eclipse.jetty.server.NetworkTrafficServerConnector} instead.
* <p>{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has
* been started without causing {@link ConcurrentModificationException}s.</p>
*/ */
public class NetworkTrafficSelectChannelConnector extends ServerConnector @Deprecated
public class NetworkTrafficSelectChannelConnector extends NetworkTrafficServerConnector
{ {
private final List<NetworkTrafficListener> listeners = new CopyOnWriteArrayList<NetworkTrafficListener>();
public NetworkTrafficSelectChannelConnector(Server server) public NetworkTrafficSelectChannelConnector(Server server)
{ {
this(server,null,null,null,0,0,new HttpConnectionFactory()); super(server);
} }
public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory) public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory)
{ {
super(server,sslContextFactory,connectionFactory); super(server, connectionFactory, sslContextFactory);
} }
public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory) public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory)
@ -62,8 +48,7 @@ public class NetworkTrafficSelectChannelConnector extends ServerConnector
super(server, connectionFactory); super(server, connectionFactory);
} }
public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories)
ConnectionFactory... factories)
{ {
super(server, executor, scheduler, pool, acceptors, selectors, factories); super(server, executor, scheduler, pool, acceptors, selectors, factories);
} }
@ -72,29 +57,4 @@ public class NetworkTrafficSelectChannelConnector extends ServerConnector
{ {
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

@ -51,6 +51,12 @@ public class HashedSession extends AbstractSession
* data stored in the session that is not serializable. */ * data stored in the session that is not serializable. */
private transient boolean _saveFailed = false; 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) protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request)
{ {
@ -68,7 +74,7 @@ public class HashedSession extends AbstractSession
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
protected void checkValid() protected void checkValid()
{ {
if (_hashSessionManager._idleSavePeriodMs!=0) if (!_deIdleFailed && _hashSessionManager._idleSavePeriodMs!=0)
deIdle(); deIdle();
super.checkValid(); super.checkValid();
} }
@ -196,7 +202,7 @@ public class HashedSession extends AbstractSession
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public synchronized void deIdle() public synchronized void deIdle()
{ {
if (isIdled()) if (isIdled() && !_deIdleFailed)
{ {
// Access now to prevent race with idling period // Access now to prevent race with idling period
access(System.currentTimeMillis()); access(System.currentTimeMillis());
@ -225,6 +231,7 @@ public class HashedSession extends AbstractSession
} }
catch (Exception e) catch (Exception e)
{ {
deIdleFailed();
LOG.warn("Problem de-idling session " + super.getId(), e); LOG.warn("Problem de-idling session " + super.getId(), e);
if (fis != null) IO.close(fis);//Must ensure closed before invalidate if (fis != null) IO.close(fis);//Must ensure closed before invalidate
invalidate(); invalidate();
@ -265,4 +272,15 @@ public class HashedSession extends AbstractSession
_saveFailed = true; _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) if (_dbAdaptor == null)
throw new IllegalStateException ("No DBAdaptor"); throw new IllegalStateException ("No DBAdaptor");
String longType = _dbAdaptor.getLongType(); 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) private void checkNotNull(String s)

View File

@ -95,49 +95,49 @@ public class JDBCSessionManager extends AbstractSessionManager
/** /**
* If dirty, session needs to be (re)persisted * 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 * 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 * 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 * 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 * 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) * 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 * 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) * 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 @Override
public void setAttribute (String name, Object value) public void setAttribute (String name, Object value)
{ {
_dirty = (updateAttribute(name, value) || _dirty); updateAttribute(name, value);
_dirty = true;
} }
@Override @Override
@ -494,6 +495,7 @@ public class JDBCSessionManager extends AbstractSessionManager
" interval="+(_saveIntervalSec * 1000L)); " interval="+(_saveIntervalSec * 1000L));
else else
LOG.debug("getSession("+idInCluster+"): in session map, "+ LOG.debug("getSession("+idInCluster+"): in session map, "+
" hashcode="+memSession.hashCode()+
" now="+now+ " now="+now+
" lastSaved="+(memSession==null?0:memSession._lastSaved)+ " lastSaved="+(memSession==null?0:memSession._lastSaved)+
" interval="+(_saveIntervalSec * 1000L)+ " interval="+(_saveIntervalSec * 1000L)+
@ -565,8 +567,12 @@ public class JDBCSessionManager extends AbstractSessionManager
} }
else 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); LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
} }
}
else else
{ {
//No session in db with matching id and context path. //No session in db with matching id and context path.
@ -770,6 +776,20 @@ public class JDBCSessionManager extends AbstractSessionManager
return new Session(request); 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 /** Remove session from manager
* @param session The session to remove * @param session The session to remove
@ -892,7 +912,7 @@ public class JDBCSessionManager extends AbstractSessionManager
{ {
maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager 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.getCreateTimeColumn()),
result.getLong(_sessionTableSchema.getAccessTimeColumn()), result.getLong(_sessionTableSchema.getAccessTimeColumn()),
maxInterval); maxInterval);
@ -963,7 +983,7 @@ public class JDBCSessionManager extends AbstractSessionManager
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
connection.setAutoCommit(true); connection.setAutoCommit(true);
statement.setString(1, rowId); //rowId 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(3, session.getCanonicalContext()); //context path
statement.setString(4, session.getVirtualHost()); //first vhost statement.setString(4, session.getVirtualHost()); //first vhost
statement.setString(5, getSessionIdManager().getWorkerName());//my node id statement.setString(5, getSessionIdManager().getWorkerName());//my node id
@ -1011,7 +1031,7 @@ public class JDBCSessionManager extends AbstractSessionManager
{ {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
connection.setAutoCommit(true); connection.setAutoCommit(true);
statement.setString(1, data.getId()); statement.setString(1, data.getClusterId());
statement.setString(2, getSessionIdManager().getWorkerName());//my node id statement.setString(2, getSessionIdManager().getWorkerName());//my node id
statement.setLong(3, data.getAccessed());//accessTime statement.setLong(3, data.getAccessed());//accessTime
statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime
@ -1085,7 +1105,7 @@ public class JDBCSessionManager extends AbstractSessionManager
data.setLastSaved(now); data.setLastSaved(now);
} }
if (LOG.isDebugEnabled()) 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

@ -547,6 +547,22 @@ public class HttpOutputTest
assertThat(response,containsString("400\tThis is a big file")); 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 static class ContentHandler extends AbstractHandler
{ {
boolean _writeLengthIfKnown=true; boolean _writeLengthIfKnown=true;
@ -664,7 +680,6 @@ public class HttpOutputTest
BufferUtil.flipToFlush(_byteBuffer,0); BufferUtil.flipToFlush(_byteBuffer,0);
out.write(_byteBuffer); out.write(_byteBuffer);
} }
Assert.assertFalse(out.isReady());
} }
@Override @Override

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -31,7 +28,6 @@ import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -39,25 +35,27 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.NetworkTrafficListener; import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.junit.After; import org.junit.After;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@Ignore @Ignore
public class NetworkTrafficListenerTest public class NetworkTrafficListenerTest
{ {
private static final byte END_OF_CONTENT = '~'; private static final byte END_OF_CONTENT = '~';
private Server server; private Server server;
private NetworkTrafficSelectChannelConnector connector; private NetworkTrafficServerConnector connector;
public void initConnector(Handler handler) throws Exception public void initConnector(Handler handler) throws Exception
{ {
server = new Server(); 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().setSendDateHeader(false);
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false); connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
server.addConnector(connector); 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 CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>(""); final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1); final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() 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 CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>(""); final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(2); final CountDownLatch outgoingLatch = new CountDownLatch(2);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() 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 CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>(""); final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(4); final CountDownLatch outgoingLatch = new CountDownLatch(4);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() 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 CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<String>(""); final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1); final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{ {
@ -393,8 +391,8 @@ public class NetworkTrafficListenerTest
} }
}); });
final AtomicReference<String> incomingData = new AtomicReference<String>(""); final AtomicReference<String> incomingData = new AtomicReference<>("");
final AtomicReference<String> outgoingData = new AtomicReference<String>(""); final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1); final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() 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; 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; String key=pathInContext==null?servletHolder.getName():pathInContext;
int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType()); 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) if (_chainLRU[FilterMapping.REQUEST]!=null)
{ {

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.servlet; package org.eclipse.jetty.servlet;
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -28,14 +26,18 @@ import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ReadListener; import javax.servlet.ReadListener;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletInputStream; import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener; import javax.servlet.WriteListener;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -52,12 +54,14 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
// TODO need these on SPDY as well! // TODO need these on SPDY as well!
public class AsyncServletIOTest public class AsyncServletIOTest
{ {
protected AsyncIOServlet _servlet=new AsyncIOServlet(); protected AsyncIOServlet _servlet0=new AsyncIOServlet();
protected AsyncIOServlet2 _servlet2=new AsyncIOServlet2();
protected int _port; protected int _port;
protected Server _server = new Server(); protected Server _server = new Server();
protected ServletHandler _servletHandler; protected ServletHandler _servletHandler;
protected ServerConnector _connector; protected ServerConnector _connector;
@ -70,13 +74,20 @@ public class AsyncServletIOTest
_connector = new ServerConnector(_server,new HttpConnectionFactory(http_config)); _connector = new ServerConnector(_server,new HttpConnectionFactory(http_config));
_server.setConnectors(new Connector[]{ _connector }); _server.setConnectors(new Connector[]{ _connector });
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS); ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/ctx"); context.setContextPath("/ctx");
_server.setHandler(context); _server.setHandler(context);
_servletHandler=context.getServletHandler(); _servletHandler=context.getServletHandler();
ServletHolder holder=new ServletHolder(_servlet);
ServletHolder holder=new ServletHolder(_servlet0);
holder.setAsyncSupported(true); holder.setAsyncSupported(true);
_servletHandler.addServletWithMapping(holder,"/path/*"); _servletHandler.addServletWithMapping(holder,"/path/*");
ServletHolder holder2=new ServletHolder(_servlet2);
holder.setAsyncSupported(true);
_servletHandler.addServletWithMapping(holder2,"/path2/*");
_server.start(); _server.start();
_port=_connector.getLocalPort(); _port=_connector.getLocalPort();
@ -146,15 +157,52 @@ public class AsyncServletIOTest
process("Hello!!!\r\n",10); process("Hello!!!\r\n",10);
} }
@Test
protected void assertContains(String content,String response) public void testAsync2() throws Exception
{ {
Assert.assertThat(response,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;
} }
protected void assertNotContains(String content,String response) // Get body slowly
while (true)
{ {
Assert.assertThat(response,Matchers.not(Matchers.containsString(content))); 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 public synchronized List<String> process(String content,int... writes) throws Exception
@ -183,14 +231,14 @@ public class AsyncServletIOTest
.append("Connection: close\r\n"); .append("Connection: close\r\n");
if (content!=null) 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"); .append("Content-Type: text/plain\r\n");
request.append("\r\n"); request.append("\r\n");
int port=_port; int port=_port;
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
try (Socket socket = new Socket("localhost",port);) try (Socket socket = new Socket("localhost",port))
{ {
socket.setSoTimeout(1000000); socket.setSoTimeout(1000000);
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
@ -266,9 +314,9 @@ public class AsyncServletIOTest
private static final long serialVersionUID = -8161977157098646562L; private static final long serialVersionUID = -8161977157098646562L;
public AsyncIOServlet() public AsyncIOServlet()
{} {
}
/* ------------------------------------------------------------ */
@Override @Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{ {
@ -298,7 +346,7 @@ public class AsyncServletIOTest
throw new IllegalStateException(); throw new IllegalStateException();
// System.err.println("ODA"); // System.err.println("ODA");
while (in.isReady()) while (in.isReady() && !in.isFinished())
{ {
_oda.incrementAndGet(); _oda.incrementAndGet();
int len=in.read(_buf); 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); return Collections.unmodifiableCollection(sessions);
} }
@Override
protected void dumpThis(Appendable out) throws IOException
{
super.dumpThis(out);
dump(out, "", sessions);
}
private class ClientSelectorManager extends SelectorManager private class ClientSelectorManager extends SelectorManager
{ {
private ClientSelectorManager(Executor executor, Scheduler scheduler) private ClientSelectorManager(Executor executor, Scheduler scheduler)

View File

@ -180,6 +180,7 @@ public class Flusher
// Has the stream been reset for this data frame ? // Has the stream been reset for this data frame ?
if (stream != null && stream.isReset() && frameBytes instanceof StandardSession.DataFrameBytes) if (stream != null && stream.isReset() && frameBytes instanceof StandardSession.DataFrameBytes)
{ {
// TODO: notify from within sync block !
frameBytes.failed(new StreamException(frameBytes.getStream().getId(), frameBytes.failed(new StreamException(frameBytes.getStream().getId(),
StreamStatus.INVALID_STREAM, "Stream: " + frameBytes.getStream() + " is reset!")); StreamStatus.INVALID_STREAM, "Stream: " + frameBytes.getStream() + " is reset!"));
continue; 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; package org.eclipse.jetty.spdy.server;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Queue; import java.util.Queue;
@ -182,6 +183,13 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
return Collections.unmodifiableCollection(sessions); 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 class ServerSPDYConnection extends SPDYConnection implements Runnable
{ {
private final ServerSessionFrameListener listener; 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.QueuedThreadPool;
import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.Scheduler;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test;
public class SynDataReplyDataLoadTest extends AbstractTest 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); private static final Logger logger = Log.getLogger(SynDataReplyDataLoadTest.class);
@Test(timeout = TIMEOUT) @Test(timeout = TIMEOUT)
@ -104,14 +104,20 @@ public class SynDataReplyDataLoadTest extends AbstractTest
}; };
} }
}; };
short spdyVersion = SPDY.V2;
long idleTimeout = 2 * TIMEOUT;
server = newServer(); server = newServer();
connector = new ServerConnector(server, null, null, serverBufferPool, 1, 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(); QueuedThreadPool clientExecutor = new QueuedThreadPool();
clientExecutor.setName(clientExecutor.getName() + "-client"); clientExecutor.setName(clientExecutor.getName() + "-client");
clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, 30000); clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, idleTimeout);
final Session session = startClient(SPDY.V3, startServer(SPDY.V3, listener), null); final Session session = startClient(spdyVersion, startServer(spdyVersion, listener), null);
final Thread testThread = Thread.currentThread(); final Thread testThread = Thread.currentThread();
Runnable timeout = new Runnable() 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(); long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks); List<Future<Object>> futures = threadPool.invokeAll(tasks);
@ -172,7 +178,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
long end = System.nanoTime(); long end = System.nanoTime();
System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin)); System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
} }
timeoutTask.cancel(); syncTimeoutTask.cancel();
tasks.clear(); tasks.clear();
for (int i = 0; i < count; ++i) 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(); long begin = System.nanoTime();
List<Future<Object>> futures = threadPool.invokeAll(tasks); List<Future<Object>> futures = threadPool.invokeAll(tasks);
@ -197,7 +203,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest
long end = System.nanoTime(); long end = System.nanoTime();
System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin)); System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
} }
timeoutTask.cancel(); asyncTimeoutTask.cancel();
threadPool.shutdown(); threadPool.shutdown();
Assert.assertEquals(0, leaks.get()); 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 private void synCompletedData(Session session, Fields headers, int iterations) throws Exception
{ {
final Map<Integer, Integer> counter = new ConcurrentHashMap<>(iterations); 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) for (int i = 0; i < iterations; ++i)
{ {
final AtomicInteger count = new AtomicInteger(2); final AtomicInteger count = new AtomicInteger(2);
@ -218,7 +225,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
public void onReply(Stream stream, ReplyInfo replyInfo) public void onReply(Stream stream, ReplyInfo replyInfo)
{ {
Assert.assertEquals(2, count.getAndDecrement()); Assert.assertEquals(2, count.getAndDecrement());
latch.countDown(); requestsLatch.countDown();
} }
@Override @Override
@ -230,7 +237,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
{ {
Assert.assertEquals(1, count.getAndDecrement()); Assert.assertEquals(1, count.getAndDecrement());
counter.remove(index); counter.remove(index);
latch.countDown(); requestsLatch.countDown();
} }
} }
}, new Promise.Adapter<Stream>() }, 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()); 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.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.spdy.LEVEL=WARN #org.eclipse.jetty.spdy.LEVEL=DEBUG

View File

@ -39,6 +39,13 @@ import java.util.regex.Pattern;
public class Modules implements Iterable<Module> public class Modules implements Iterable<Module>
{ {
private Map<String, Module> modules = new HashMap<>(); 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 int maxDepth = -1;
private Set<String> asNameSet(Set<Module> moduleSet) private Set<String> asNameSet(Set<Module> moduleSet)
@ -110,7 +117,7 @@ public class Modules implements Iterable<Module>
if (parent == null) if (parent == null)
{ {
System.err.printf("WARNING: module not found [%s]%n",parentName); StartLog.debug("module not found [%s]%n",parentName);
} }
else else
{ {
@ -124,7 +131,7 @@ public class Modules implements Iterable<Module>
Module optional = get(optionalParentName); Module optional = get(optionalParentName);
if (optional == null) 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()) 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()) for (Module parent : module.getParentEdges())
{ {
ret.add(parent); ret.put(parent.getName(), parent);
findParents(parent,ret); findParents(parent,ret);
} }
} }
@ -380,7 +387,7 @@ public class Modules implements Iterable<Module>
{ {
for (String parent : m.getParentNames()) for (String parent : m.getParentNames())
{ {
if (modules.containsKey(parent)) if (modules.containsKey(parent) || missingModules.contains(parent))
{ {
continue; // found. skip it. continue; // found. skip it.
} }
@ -392,9 +399,17 @@ public class Modules implements Iterable<Module>
for (String missingParent : missingParents) for (String missingParent : missingParents)
{ {
File file = basehome.getFile("modules/" + missingParent + ".mod"); File file = basehome.getFile("modules/" + missingParent + ".mod");
if ( FS.canReadFile(file) )
{
Module module = registerModule(basehome,args,file); Module module = registerModule(basehome,args,file);
updateParentReferencesTo(module); 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() public List<Module> resolveEnabled()
{ {
Set<Module> active = new HashSet<Module>(); Map<String, Module> active = new HashMap<String,Module>();
for (Module module : modules.values()) 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<>(); List<Module> ordered = new ArrayList<>();
ordered.addAll(active); ordered.addAll(active.values());
Collections.sort(ordered,new Module.DepthComparator()); Collections.sort(ordered,new Module.DepthComparator());
return ordered; return ordered;
} }
public Set<String> resolveParentModulesOf(String moduleName) public Set<String> resolveParentModulesOf(String moduleName)
{ {
Set<Module> ret = new HashSet<>(); Map<String,Module> ret = new HashMap<>();
Module module = get(moduleName); Module module = get(moduleName);
findParents(module,ret); findParents(module,ret);
return asNameSet(ret); return ret.keySet();
} }
private String toIndent(int depth) private String toIndent(int depth)

View File

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

View File

@ -18,10 +18,7 @@
package org.eclipse.jetty.start; package org.eclipse.jetty.start;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.*;
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.File;
import java.io.IOException; import java.io.IOException;
@ -45,6 +42,15 @@ public class BaseHomeTest
Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray())); 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 @Test
public void testGetFile_OnlyHome() throws IOException public void testGetFile_OnlyHome() throws IOException
{ {
@ -76,6 +82,7 @@ public class BaseHomeTest
expected.add("${jetty.home}/start.d/jsp.ini"); expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.home}/start.d/logging.ini"); expected.add("${jetty.home}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini"); expected.add("${jetty.home}/start.d/ssl.ini");
toOsSeparators(expected);
assertFileList(hb,"Files found",expected,files); assertFileList(hb,"Files found",expected,files);
} }
@ -95,6 +102,7 @@ public class BaseHomeTest
expected.add("${jetty.home}/start.d/jsp.ini"); expected.add("${jetty.home}/start.d/jsp.ini");
expected.add("${jetty.home}/start.d/logging.ini"); expected.add("${jetty.home}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini"); expected.add("${jetty.home}/start.d/ssl.ini");
toOsSeparators(expected);
assertFileList(hb,"Files found",expected,files); assertFileList(hb,"Files found",expected,files);
} }
@ -115,6 +123,7 @@ public class BaseHomeTest
expected.add("${jetty.base}/start.d/logging.ini"); expected.add("${jetty.base}/start.d/logging.ini");
expected.add("${jetty.home}/start.d/ssl.ini"); expected.add("${jetty.home}/start.d/ssl.ini");
expected.add("${jetty.base}/start.d/myapp.ini"); expected.add("${jetty.base}/start.d/myapp.ini");
toOsSeparators(expected);
assertFileList(hb,"Files found",expected,files); assertFileList(hb,"Files found",expected,files);
} }

View File

@ -60,7 +60,7 @@ public class ConfigurationAssert
{ {
if (line.startsWith("XML|")) if (line.startsWith("XML|"))
{ {
expectedXmls.add(getValue(line)); expectedXmls.add(FS.separators(getValue(line)));
} }
} }
List<String> actualXmls = new ArrayList<>(); List<String> actualXmls = new ArrayList<>();
@ -76,7 +76,7 @@ public class ConfigurationAssert
{ {
if (line.startsWith("LIB|")) if (line.startsWith("LIB|"))
{ {
expectedLibs.add(getValue(line)); expectedLibs.add(FS.separators(getValue(line)));
} }
} }
List<String> actualLibs = new ArrayList<>(); List<String> actualLibs = new ArrayList<>();

View File

@ -61,12 +61,25 @@ public class TestUseCases
assertUseCase("home","base.jmx","assert-jmx.txt"); 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 @Test
public void testWithSpdy() throws Exception public void testWithSpdy() throws Exception
{ {
assertUseCase("home","base.enable.spdy","assert-enable-spdy.txt","java.version=1.7.0_21"); 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 @Test
public void testWithDatabase() throws Exception 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.PhantomReference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; 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

@ -398,6 +398,9 @@ public class WebInfConfiguration extends AbstractConfiguration
else else
web_app=context.getBaseResource(); web_app=context.getBaseResource();
if (web_app == null)
throw new IllegalStateException("No resourceBase or war set for context");
// Accept aliases for WAR files // Accept aliases for WAR files
if (web_app.getAlias() != null) if (web_app.getAlias() != null)
{ {
@ -603,7 +606,7 @@ public class WebInfConfiguration extends AbstractConfiguration
if (resource == null) if (resource == null)
{ {
if (context.getWar()==null || context.getWar().length()==0) 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 // Set dir or WAR
resource = context.newResource(context.getWar()); resource = context.newResource(context.getWar());
@ -621,7 +624,8 @@ public class WebInfConfiguration extends AbstractConfiguration
} }
catch (Exception e) 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 //Context name

View File

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

View File

@ -18,9 +18,9 @@
package org.eclipse.jetty.websocket.jsr356; package org.eclipse.jetty.websocket.jsr356;
import java.net.HttpCookie;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.websocket.ClientEndpointConfig.Configurator; import javax.websocket.ClientEndpointConfig.Configurator;
import org.eclipse.jetty.websocket.api.UpgradeRequest; import org.eclipse.jetty.websocket.api.UpgradeRequest;
@ -46,7 +46,23 @@ public class JsrUpgradeListener implements UpgradeListener
Map<String, List<String>> headers = request.getHeaders(); Map<String, List<String>> headers = request.getHeaders();
configurator.beforeRequest(headers); 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 @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.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient; 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.DateDecoder;
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.TimeEncoder; import org.eclipse.jetty.websocket.jsr356.server.samples.beans.TimeEncoder;
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.ConfiguredEchoSocket; 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.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
/** /**
@ -45,6 +48,9 @@ import org.junit.Test;
*/ */
public class AnnotatedServerEndpointTest public class AnnotatedServerEndpointTest
{ {
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private static WSServer server; private static WSServer server;
@BeforeClass @BeforeClass
@ -72,7 +78,7 @@ public class AnnotatedServerEndpointTest
private void assertResponse(String message, String... expectedTexts) throws Exception private void assertResponse(String message, String... expectedTexts) throws Exception
{ {
WebSocketClient client = new WebSocketClient(); WebSocketClient client = new WebSocketClient(bufferPool);
try try
{ {
client.start(); client.start();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -95,17 +95,27 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
this(null,executor); this(null,executor);
} }
public WebSocketClient(ByteBufferPool bufferPool)
{
this(null,null,bufferPool);
}
public WebSocketClient(SslContextFactory sslContextFactory) public WebSocketClient(SslContextFactory sslContextFactory)
{ {
this(sslContextFactory,null); this(sslContextFactory,null);
} }
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor) public WebSocketClient(SslContextFactory sslContextFactory, Executor executor)
{
this(sslContextFactory,executor,new MappedByteBufferPool());
}
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool)
{ {
this.executor = executor; this.executor = executor;
this.sslContextFactory = sslContextFactory; this.sslContextFactory = sslContextFactory;
this.policy = WebSocketPolicy.newClientPolicy(); this.policy = WebSocketPolicy.newClientPolicy();
this.bufferPool = new MappedByteBufferPool(); this.bufferPool = bufferPool;
this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool); this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool);
this.masker = new RandomMasker(); this.masker = new RandomMasker();
this.eventDriverFactory = new EventDriverFactory(policy); this.eventDriverFactory = new EventDriverFactory(policy);
@ -254,7 +264,7 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
} }
super.doStop(); 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.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.common.test.BlockheadServer; 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.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -41,13 +43,16 @@ public class BadNetworkTest
@Rule @Rule
public TestTracker tt = new TestTracker(); public TestTracker tt = new TestTracker();
@Rule
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
private BlockheadServer server; private BlockheadServer server;
private WebSocketClient client; private WebSocketClient client;
@Before @Before
public void startClient() throws Exception public void startClient() throws Exception
{ {
client = new WebSocketClient(); client = new WebSocketClient(bufferPool);
client.getPolicy().setIdleTimeout(250); client.getPolicy().setIdleTimeout(250);
client.start(); client.start();
} }

View File

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

View File

@ -29,11 +29,13 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; 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.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.OpCode; 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. * 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 final Random __random = new Random();
private static LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("TestClient",new MappedByteBufferPool());
private final String _host; private final String _host;
private final int _port; private final int _port;
private final String _protocol; private final String _protocol;
@ -172,7 +176,7 @@ public class TestClient
} }
TestClient[] client = new TestClient[clients]; TestClient[] client = new TestClient[clients];
WebSocketClient wsclient = new WebSocketClient(); WebSocketClient wsclient = new WebSocketClient(bufferPool);
try try
{ {
wsclient.start(); wsclient.start();
@ -250,6 +254,7 @@ public class TestClient
wsclient.stop(); wsclient.stop();
} }
bufferPool.assertNoLeaks();
} }
private static void usage(String[] args) private static void usage(String[] args)

View File

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

View File

@ -56,37 +56,42 @@ import org.eclipse.jetty.websocket.common.frames.TextFrame;
*/ */
public abstract class WebSocketFrame implements Frame public abstract class WebSocketFrame implements Frame
{ {
public static WebSocketFrame copy(Frame copy) public static WebSocketFrame copy(Frame original)
{ {
WebSocketFrame frame = null; WebSocketFrame copy;
switch (copy.getOpCode()) switch (original.getOpCode())
{ {
case OpCode.BINARY: case OpCode.BINARY:
frame = new BinaryFrame(); copy = new BinaryFrame();
break; break;
case OpCode.TEXT: case OpCode.TEXT:
frame = new TextFrame(); copy = new TextFrame();
break; break;
case OpCode.CLOSE: case OpCode.CLOSE:
frame = new CloseFrame(); copy = new CloseFrame();
break; break;
case OpCode.CONTINUATION: case OpCode.CONTINUATION:
frame = new ContinuationFrame(); copy = new ContinuationFrame();
break; break;
case OpCode.PING: case OpCode.PING:
frame = new PingFrame(); copy = new PingFrame();
break; break;
case OpCode.PONG: case OpCode.PONG:
frame = new PongFrame(); copy = new PongFrame();
break; break;
default: 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); copy.copyHeaders(original);
frame.setPayload(copy.getPayload()); ByteBuffer payload = original.getPayload();
if (payload != null)
return frame; {
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(); public abstract void assertValid();
protected void copy(WebSocketFrame copy, ByteBuffer payload)
{
copyHeaders(copy);
setPayload(payload);
}
protected void copyHeaders(Frame frame) protected void copyHeaders(Frame frame)
{ {
finRsvOp = 0x00; finRsvOp = 0x00;
@ -348,13 +347,7 @@ public abstract class WebSocketFrame implements Frame
*/ */
public WebSocketFrame setPayload(ByteBuffer buf) public WebSocketFrame setPayload(ByteBuffer buf)
{ {
if (buf == null) data = buf;
{
data = null;
return this;
}
data = buf.slice();
return this; return this;
} }

View File

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

View File

@ -78,13 +78,20 @@ public class DataFrame extends WebSocketFrame
return true; 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) if (pool!=null)
{ {
pool.release(this.data); pool.release(this.data);
this.data=null;
} }
super.reset();
} }
/** /**

View File

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

View File

@ -41,25 +41,19 @@ public class MessageInputStream extends InputStream implements MessageAppender
private static final Logger LOG = Log.getLogger(MessageInputStream.class); private static final Logger LOG = Log.getLogger(MessageInputStream.class);
// EOF (End of Buffers) // EOF (End of Buffers)
private final static ByteBuffer EOF = ByteBuffer.allocate(0).asReadOnlyBuffer(); 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 final BlockingDeque<ByteBuffer> buffers = new LinkedBlockingDeque<>();
private AtomicBoolean closed = new AtomicBoolean(false); private AtomicBoolean closed = new AtomicBoolean(false);
private final long timeoutMs;
private ByteBuffer activeBuffer = null; private ByteBuffer activeBuffer = null;
private long timeoutMs = -1;
public MessageInputStream(LogicalConnection connection) public MessageInputStream(LogicalConnection connection)
{ {
this.connection = connection; this(connection, -1);
this.timeoutMs = -1; // disabled
} }
public MessageInputStream(LogicalConnection connection, int timeoutMs) public MessageInputStream(LogicalConnection connection, int timeoutMs)
{ {
this.connection = connection;
this.timeoutMs = timeoutMs; this.timeoutMs = timeoutMs;
} }
@ -71,16 +65,22 @@ public class MessageInputStream extends InputStream implements MessageAppender
LOG.debug("appendMessage(ByteBuffer,{}): {}",fin,BufferUtil.toDetailString(framePayload)); 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()) if (closed.get())
{ {
return; 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 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) catch (InterruptedException e)
{ {
@ -141,7 +141,7 @@ public class MessageInputStream extends InputStream implements MessageAppender
// grab a fresh buffer // grab a fresh buffer
while (activeBuffer == null || !activeBuffer.hasRemaining()) while (activeBuffer == null || !activeBuffer.hasRemaining())
{ {
if (timeoutMs == -1) if (timeoutMs < 0)
{ {
// infinite take // infinite take
activeBuffer = buffers.take(); activeBuffer = buffers.take();

View File

@ -143,6 +143,7 @@ public class MessageOutputStream extends OutputStream
// block success // block success
frameCount++; frameCount++;
frame.setIsContinuation(); frame.setIsContinuation();
BufferUtil.flipToFill(buffer);
} }
catch (IOException e) catch (IOException e)
{ {

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