Merge branch 'master' into release-9
This commit is contained in:
commit
206b2ebf35
|
@ -2,7 +2,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<version>9.1.2-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<configuration>
|
||||
<excludes>**/MANIFEST.MF,javax/**</excludes>
|
||||
<excludeArtifactIds>javax</excludeArtifactIds>
|
||||
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
|
||||
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm</excludeGroupIds>
|
||||
<outputDirectory>${project.build.directory}/classes</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes>
|
||||
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
|
||||
<excludeArtifactIds>javax</excludeArtifactIds>
|
||||
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn</excludeGroupIds>
|
||||
<excludeGroupIds>javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm</excludeGroupIds>
|
||||
<outputDirectory>${project.build.directory}/sources</outputDirectory>
|
||||
<overWriteReleases>true</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
|
|
0
jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java
Executable file → Normal file
0
jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java
Executable file → Normal file
|
@ -27,6 +27,7 @@ import org.eclipse.jetty.client.api.Connection;
|
|||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
|
@ -179,9 +180,12 @@ public class HttpProxy extends ProxyConfiguration.Proxy
|
|||
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
|
||||
HttpClient client = destination.getHttpClient();
|
||||
ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
|
||||
org.eclipse.jetty.io.Connection oldConnection = endPoint.getConnection();
|
||||
HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
|
||||
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
|
||||
Helper.replaceConnection(oldConnection, newConnection);
|
||||
// Avoid setting fill interest in the old Connection,
|
||||
// without closing the underlying EndPoint.
|
||||
oldConnection.softClose();
|
||||
LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
|
||||
}
|
||||
catch (Throwable x)
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.client.http;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.eclipse.jetty.client.HttpChannel;
|
||||
import org.eclipse.jetty.client.HttpExchange;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
|
@ -79,28 +77,13 @@ public class HttpChannelOverHTTP extends HttpChannel
|
|||
public void exchangeTerminated(Result result)
|
||||
{
|
||||
super.exchangeTerminated(result);
|
||||
|
||||
if (result.isSucceeded())
|
||||
{
|
||||
HttpFields responseHeaders = result.getResponse().getHeaders();
|
||||
Enumeration<String> values = responseHeaders.getValues(HttpHeader.CONNECTION.asString(), ",");
|
||||
if (values != null)
|
||||
{
|
||||
while (values.hasMoreElements())
|
||||
{
|
||||
if (HttpHeaderValue.CLOSE.asString().equalsIgnoreCase(values.nextElement()))
|
||||
{
|
||||
connection.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
connection.release();
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean close = result.isFailed();
|
||||
HttpFields responseHeaders = result.getResponse().getHeaders();
|
||||
close |= responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
|
||||
if (close)
|
||||
connection.close();
|
||||
}
|
||||
else
|
||||
connection.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -76,7 +76,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
fillInterested();
|
||||
}
|
||||
|
||||
protected boolean isClosed()
|
||||
public boolean isClosed()
|
||||
{
|
||||
return closed.get();
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (closed.compareAndSet(false, true))
|
||||
if (softClose())
|
||||
{
|
||||
getHttpDestination().close(this);
|
||||
getEndPoint().shutdownOutput();
|
||||
|
@ -130,6 +130,11 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
}
|
||||
}
|
||||
|
||||
public boolean softClose()
|
||||
{
|
||||
return closed.compareAndSet(false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -48,6 +48,7 @@ import javax.servlet.ServletOutputStream;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Destination;
|
||||
|
@ -58,12 +59,16 @@ import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
|
|||
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
|
||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.client.util.FutureResponseListener;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.Assert;
|
||||
|
@ -1073,4 +1078,86 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP10WithKeepAliveAndContentLength() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
// Send the headers at this point, then write the content
|
||||
byte[] content = "TEST".getBytes("UTF-8");
|
||||
response.setContentLength(content.length);
|
||||
response.flushBuffer();
|
||||
response.getOutputStream().write(content);
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.version(HttpVersion.HTTP_1_0)
|
||||
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP10WithKeepAliveAndNoContentLength() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
// Send the headers at this point, then write the content
|
||||
response.flushBuffer();
|
||||
response.getOutputStream().print("TEST");
|
||||
}
|
||||
});
|
||||
|
||||
FuturePromise<Connection> promise = new FuturePromise<>();
|
||||
Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort());
|
||||
destination.newConnection(promise);
|
||||
try (Connection connection = promise.get(5, TimeUnit.SECONDS))
|
||||
{
|
||||
long timeout = 5000;
|
||||
Request request = client.newRequest(destination.getHost(), destination.getPort())
|
||||
.scheme(destination.getScheme())
|
||||
.version(HttpVersion.HTTP_1_0)
|
||||
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
|
||||
.timeout(timeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
FutureResponseListener listener = new FutureResponseListener(request);
|
||||
connection.send(request, listener);
|
||||
ContentResponse response = listener.get(2 * timeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
// The parser notifies end-of-content and therefore the CompleteListener
|
||||
// before closing the connection, so we need to wait before checking
|
||||
// that the connection is closed to avoid races.
|
||||
Thread.sleep(1000);
|
||||
Assert.assertTrue(((HttpConnectionOverHTTP)connection).isClosed());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP10WithKeepAliveAndNoContent() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.version(HttpVersion.HTTP_1_0)
|
||||
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -427,7 +427,7 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
|
||||
<includeArtifactIds>javax.activation,javax.mail.glassfish</includeArtifactIds>
|
||||
<includeArtifactIds>javax.mail.glassfish</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jndi</outputDirectory>
|
||||
</configuration>
|
||||
|
@ -440,11 +440,38 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain</includeGroupIds>
|
||||
<includeArtifactIds>javax.servlet.jsp.jstl,org.apache.taglibs.standard.glassfish,org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el</includeArtifactIds>
|
||||
<includeArtifactIds>org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-jstl-api</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.orbit</includeGroupIds>
|
||||
<includeArtifactIds>javax.servlet.jsp.jstl</includeArtifactIds>
|
||||
<prependGroupId>true</prependGroupId>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-jstl-impl</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.glassfish.web</includeGroupIds>
|
||||
<includeArtifactIds>javax.servlet.jsp.jstl</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-jaspi-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
|
@ -551,29 +578,27 @@
|
|||
</build>
|
||||
<dependencies>
|
||||
<!-- Orbit Deps -->
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.activation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.mail.glassfish</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>javax.transaction-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.security.auth.message</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.apache.taglibs.standard.glassfish</artifactId>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>javax.transaction-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.web</groupId>
|
||||
<artifactId>javax.servlet.jsp.jstl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -105,7 +105,6 @@ public class HttpGenerator
|
|||
_persistent = null;
|
||||
_contentPrepared = 0;
|
||||
_needCRLF = false;
|
||||
_noContent=false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -744,13 +743,14 @@ public class HttpGenerator
|
|||
}
|
||||
else
|
||||
{
|
||||
// No idea, so we must assume that a body is coming
|
||||
_endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
|
||||
if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
|
||||
{
|
||||
_endOfContent=EndOfContent.NO_CONTENT;
|
||||
_noContent=true;
|
||||
}
|
||||
// No idea, so we must assume that a body is coming.
|
||||
_endOfContent = EndOfContent.CHUNKED_CONTENT;
|
||||
// HTTP 1.0 does not understand chunked content, so we must use EOF content.
|
||||
// For a request with HTTP 1.0 & Connection: keep-alive
|
||||
// we *must* close the connection, otherwise the client
|
||||
// has no way to detect the end of the content.
|
||||
if (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal())
|
||||
_endOfContent = EndOfContent.EOF_CONTENT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -825,7 +825,7 @@ public class HttpGenerator
|
|||
header.put(CONNECTION_KEEP_ALIVE);
|
||||
else
|
||||
{
|
||||
header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_CLOSE.length-2);
|
||||
header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2);
|
||||
header.put((byte)',');
|
||||
header.put(StringUtil.getBytes(connection.toString()));
|
||||
header.put(CRLF);
|
||||
|
@ -833,7 +833,7 @@ public class HttpGenerator
|
|||
}
|
||||
else if (connection!=null)
|
||||
{
|
||||
header.put(CONNECTION_);
|
||||
header.put(HttpHeader.CONNECTION.getBytesColonSpace());
|
||||
header.put(StringUtil.getBytes(connection.toString()));
|
||||
header.put(CRLF);
|
||||
}
|
||||
|
@ -872,7 +872,6 @@ public class HttpGenerator
|
|||
private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
|
||||
private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
|
||||
private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
|
||||
private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: ");
|
||||
private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" ");
|
||||
private static final byte[] CRLF = StringUtil.getBytes("\015\012");
|
||||
private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -18,310 +18,33 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.either;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class HttpGeneratorServerTest
|
||||
{
|
||||
private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
|
||||
{
|
||||
@Override
|
||||
public boolean content(ByteBuffer ref)
|
||||
{
|
||||
if (_content == null)
|
||||
_content = "";
|
||||
_content += BufferUtil.toString(ref);
|
||||
ref.position(ref.limit());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void earlyEOF()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean headerComplete()
|
||||
{
|
||||
_content = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean messageComplete()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parsedHeader(HttpField field)
|
||||
{
|
||||
_hdr.add(field.getName());
|
||||
_val.add(field.getValue());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startResponse(HttpVersion version, int status, String reason)
|
||||
{
|
||||
_version = version;
|
||||
_status = status;
|
||||
_reason = reason;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void badMessage(int status, String reason)
|
||||
{
|
||||
throw new IllegalStateException(reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeaderCacheSize()
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TR
|
||||
{
|
||||
private HttpFields _fields = new HttpFields();
|
||||
private final String _body;
|
||||
private final int _code;
|
||||
String _connection;
|
||||
int _contentLength;
|
||||
String _contentType;
|
||||
private final boolean _head;
|
||||
String _other;
|
||||
String _te;
|
||||
|
||||
private TR(int code, String contentType, int contentLength, String content, boolean head)
|
||||
{
|
||||
_code = code;
|
||||
_contentType = contentType;
|
||||
_contentLength = contentLength;
|
||||
_other = "value";
|
||||
_body = content;
|
||||
_head = head;
|
||||
}
|
||||
|
||||
private String build(int version, HttpGenerator gen, String reason, String connection, String te, int nchunks) throws Exception
|
||||
{
|
||||
String response = "";
|
||||
_connection = connection;
|
||||
_te = te;
|
||||
|
||||
if (_contentType != null)
|
||||
_fields.put("Content-Type", _contentType);
|
||||
if (_contentLength >= 0)
|
||||
_fields.put("Content-Length", "" + _contentLength);
|
||||
if (_connection != null)
|
||||
_fields.put("Connection", _connection);
|
||||
if (_te != null)
|
||||
_fields.put("Transfer-Encoding", _te);
|
||||
if (_other != null)
|
||||
_fields.put("Other", _other);
|
||||
|
||||
ByteBuffer source = _body == null ? null : BufferUtil.toBuffer(_body);
|
||||
ByteBuffer[] chunks = new ByteBuffer[nchunks];
|
||||
ByteBuffer content = null;
|
||||
int c = 0;
|
||||
if (source != null)
|
||||
{
|
||||
for (int i = 0; i < nchunks; i++)
|
||||
{
|
||||
chunks[i] = source.duplicate();
|
||||
chunks[i].position(i * (source.capacity() / nchunks));
|
||||
if (i > 0)
|
||||
chunks[i - 1].limit(chunks[i].position());
|
||||
}
|
||||
content = chunks[c++];
|
||||
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
|
||||
}
|
||||
ByteBuffer header = null;
|
||||
ByteBuffer chunk = null;
|
||||
HttpGenerator.ResponseInfo info = null;
|
||||
|
||||
loop:
|
||||
while (true)
|
||||
{
|
||||
// if we have unwritten content
|
||||
if (source != null && content != null && content.remaining() == 0 && c < nchunks)
|
||||
{
|
||||
content = chunks[c++];
|
||||
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
|
||||
}
|
||||
|
||||
// Generate
|
||||
boolean last = !BufferUtil.hasContent(content);
|
||||
|
||||
HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case NEED_INFO:
|
||||
info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head);
|
||||
continue;
|
||||
|
||||
case NEED_HEADER:
|
||||
header = BufferUtil.allocate(2048);
|
||||
continue;
|
||||
|
||||
case NEED_CHUNK:
|
||||
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||
continue;
|
||||
|
||||
case FLUSH:
|
||||
if (BufferUtil.hasContent(header))
|
||||
{
|
||||
response += BufferUtil.toString(header);
|
||||
header.position(header.limit());
|
||||
}
|
||||
if (BufferUtil.hasContent(chunk))
|
||||
{
|
||||
response += BufferUtil.toString(chunk);
|
||||
chunk.position(chunk.limit());
|
||||
}
|
||||
if (BufferUtil.hasContent(content))
|
||||
{
|
||||
response += BufferUtil.toString(content);
|
||||
content.position(content.limit());
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTINUE:
|
||||
continue;
|
||||
|
||||
case SHUTDOWN_OUT:
|
||||
break;
|
||||
|
||||
case DONE:
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null ? "null" : "content") + "]";
|
||||
}
|
||||
|
||||
public HttpFields getHttpFields()
|
||||
{
|
||||
return _fields;
|
||||
}
|
||||
}
|
||||
|
||||
public final static String[] connections = {null, "keep-alive", "close", "TE, close"};
|
||||
public final static String CONTENT = "The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";
|
||||
|
||||
private final List<String> _hdr = new ArrayList<>();
|
||||
private final List<String> _val = new ArrayList<>();
|
||||
private String _content;
|
||||
private String _reason;
|
||||
private int _status;
|
||||
private HttpVersion _version;
|
||||
private final TR[] tr =
|
||||
{
|
||||
/* 0 */ new TR(200, null, -1, null, false),
|
||||
/* 1 */ new TR(200, null, -1, CONTENT, false),
|
||||
/* 2 */ new TR(200, null, CONTENT.length(), null, true),
|
||||
/* 3 */ new TR(200, null, CONTENT.length(), CONTENT, false),
|
||||
/* 4 */ new TR(200, "text/html", -1, null, true),
|
||||
/* 5 */ new TR(200, "text/html", -1, CONTENT, false),
|
||||
/* 6 */ new TR(200, "text/html", CONTENT.length(), null, true),
|
||||
/* 7 */ new TR(200, "text/html", CONTENT.length(), CONTENT, false),
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testHTTP() throws Exception
|
||||
{
|
||||
Handler handler = new Handler();
|
||||
|
||||
// Loop over HTTP versions
|
||||
for (int v = 9; v <= 11; v++)
|
||||
{
|
||||
// For each test result
|
||||
for (int r = 0; r < tr.length; r++)
|
||||
{
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
// Loop over chunks
|
||||
for (int chunks = 1; chunks <= 6; chunks++)
|
||||
{
|
||||
// Loop over Connection values
|
||||
for (int c = 0; c < (v == 11 ? connections.length : (connections.length - 1)); c++)
|
||||
{
|
||||
String t = "v=" + v + ",chunks=" + chunks + ",connection=" + connections[c] + ",tr=" + r + "=" + tr[r];
|
||||
|
||||
gen.reset();
|
||||
tr[r].getHttpFields().clear();
|
||||
|
||||
String response = tr[r].build(v, gen, "OK\r\nTest", connections[c], null, chunks);
|
||||
|
||||
if (v == 9)
|
||||
{
|
||||
assertFalse(t, gen.isPersistent());
|
||||
if (tr[r]._body != null)
|
||||
assertEquals(t, tr[r]._body, response);
|
||||
continue;
|
||||
}
|
||||
|
||||
HttpParser parser = new HttpParser(handler);
|
||||
parser.setHeadResponse(tr[r]._head);
|
||||
|
||||
parser.parseNext(BufferUtil.toBuffer(response));
|
||||
|
||||
if (tr[r]._body != null)
|
||||
assertEquals(t, tr[r]._body, this._content);
|
||||
|
||||
if (v == 10)
|
||||
assertTrue(t, gen.isPersistent() || tr[r]._contentLength >= 0 || c == 2 || c == 0);
|
||||
else
|
||||
assertTrue(t, gen.isPersistent() || c == 2 || c == 3);
|
||||
|
||||
if (v > 9)
|
||||
assertEquals("OK??Test", _reason);
|
||||
|
||||
if (_content == null)
|
||||
assertTrue(t, tr[r]._body == null);
|
||||
else
|
||||
assertThat(t, tr[r]._contentLength, either(equalTo(_content.length())).or(equalTo(-1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendServerXPoweredBy() throws Exception
|
||||
{
|
||||
ByteBuffer header = BufferUtil.allocate(8096);
|
||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.add(HttpHeader.SERVER,"SomeServer");
|
||||
fields.add(HttpHeader.X_POWERED_BY,"SomePower");
|
||||
fields.add(HttpHeader.SERVER, "SomeServer");
|
||||
fields.add(HttpHeader.X_POWERED_BY, "SomePower");
|
||||
ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false);
|
||||
String head;
|
||||
|
||||
HttpGenerator gen = new HttpGenerator(true,true);
|
||||
|
||||
HttpGenerator gen = new HttpGenerator(true, true);
|
||||
gen.generateResponse(info, header, null, null, true);
|
||||
head = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
|
@ -338,8 +61,8 @@ public class HttpGeneratorServerTest
|
|||
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
|
||||
assertThat(head, containsString("X-Powered-By: SomePower"));
|
||||
gen.reset();
|
||||
|
||||
gen = new HttpGenerator(false,false);
|
||||
|
||||
gen = new HttpGenerator(false, false);
|
||||
gen.generateResponse(info, header, null, null, true);
|
||||
head = BufferUtil.toString(header);
|
||||
BufferUtil.clear(header);
|
||||
|
@ -355,7 +78,7 @@ public class HttpGeneratorServerTest
|
|||
assertThat(head, containsString("Server: SomeServer"));
|
||||
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
|
||||
assertThat(head, containsString("X-Powered-By: SomePower"));
|
||||
gen.reset();
|
||||
gen.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -365,8 +88,7 @@ public class HttpGeneratorServerTest
|
|||
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
HttpGenerator.Result
|
||||
result = gen.generateResponse(null, null, null, null, true);
|
||||
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||
|
||||
|
@ -399,8 +121,7 @@ public class HttpGeneratorServerTest
|
|||
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
HttpGenerator.Result
|
||||
result = gen.generateResponse(null, null, null, null, true);
|
||||
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
|
||||
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||
|
||||
|
@ -454,7 +175,7 @@ public class HttpGeneratorServerTest
|
|||
out += BufferUtil.toString(content0);
|
||||
BufferUtil.clear(content0);
|
||||
|
||||
result = gen.generateResponse(null,null,chunk, content1, false);
|
||||
result = gen.generateResponse(null, null, chunk, content1, false);
|
||||
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
|
||||
out += BufferUtil.toString(chunk);
|
||||
|
@ -462,17 +183,17 @@ public class HttpGeneratorServerTest
|
|||
out += BufferUtil.toString(content1);
|
||||
BufferUtil.clear(content1);
|
||||
|
||||
result = gen.generateResponse(null,null,chunk, null, true);
|
||||
result = gen.generateResponse(null, null, chunk, null, true);
|
||||
assertEquals(HttpGenerator.Result.CONTINUE, result);
|
||||
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
|
||||
|
||||
result = gen.generateResponse(null,null,chunk, null, true);
|
||||
result = gen.generateResponse(null, null, chunk, null, true);
|
||||
assertEquals(HttpGenerator.Result.FLUSH, result);
|
||||
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
|
||||
out += BufferUtil.toString(chunk);
|
||||
BufferUtil.clear(chunk);
|
||||
|
||||
result = gen.generateResponse(null,null,chunk, null, true);
|
||||
result = gen.generateResponse(null, null, chunk, null, true);
|
||||
assertEquals(HttpGenerator.Result.DONE, result);
|
||||
assertEquals(HttpGenerator.State.END, gen.getState());
|
||||
|
||||
|
@ -495,9 +216,7 @@ public class HttpGeneratorServerTest
|
|||
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
HttpGenerator.Result
|
||||
|
||||
result = gen.generateResponse(null, null, null, content0, false);
|
||||
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
|
||||
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||
|
||||
|
@ -540,15 +259,12 @@ public class HttpGeneratorServerTest
|
|||
@Test
|
||||
public void test100ThenResponseWithContent() throws Exception
|
||||
{
|
||||
|
||||
ByteBuffer header = BufferUtil.allocate(4096);
|
||||
ByteBuffer content0 = BufferUtil.toBuffer("Hello World! ");
|
||||
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
||||
HttpGenerator gen = new HttpGenerator();
|
||||
|
||||
HttpGenerator.Result
|
||||
|
||||
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
|
||||
HttpGenerator.Result result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
|
||||
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
|
||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||
|
||||
|
@ -563,7 +279,6 @@ public class HttpGeneratorServerTest
|
|||
|
||||
assertThat(out, containsString("HTTP/1.1 100 Continue"));
|
||||
|
||||
|
||||
result = gen.generateResponse(null, null, null, content0, false);
|
||||
assertEquals(HttpGenerator.Result.NEED_INFO, result);
|
||||
assertEquals(HttpGenerator.State.START, gen.getState());
|
||||
|
@ -603,4 +318,22 @@ public class HttpGeneratorServerTest
|
|||
assertThat(out, containsString("Content-Length: 59"));
|
||||
assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionKeepAliveWithAdditionalCustomValue() throws Exception
|
||||
{
|
||||
HttpGenerator generator = new HttpGenerator();
|
||||
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
|
||||
String customValue = "test";
|
||||
fields.add(HttpHeader.CONNECTION, customValue);
|
||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_0, fields, -1, 200, "OK", false);
|
||||
ByteBuffer header = BufferUtil.allocate(4096);
|
||||
HttpGenerator.Result result = generator.generateResponse(info, header, null, null, true);
|
||||
Assert.assertSame(HttpGenerator.Result.FLUSH, result);
|
||||
String headers = BufferUtil.toString(header);
|
||||
Assert.assertTrue(headers.contains(HttpHeaderValue.KEEP_ALIVE.asString()));
|
||||
Assert.assertTrue(headers.contains(customValue));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
|
|||
LeakTrackingByteBufferPool.this.leaked(leakInfo);
|
||||
}
|
||||
};
|
||||
|
||||
private final ByteBufferPool delegate;
|
||||
|
||||
public LeakTrackingByteBufferPool(ByteBufferPool delegate)
|
||||
|
@ -51,20 +52,22 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By
|
|||
{
|
||||
ByteBuffer buffer = delegate.acquire(size, direct);
|
||||
if (!leakDetector.acquired(buffer))
|
||||
LOG.info("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
|
||||
LOG.warn("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(ByteBuffer buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
return;
|
||||
if (!leakDetector.released(buffer))
|
||||
LOG.info("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer));
|
||||
LOG.warn("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer));
|
||||
delegate.release(buffer);
|
||||
}
|
||||
|
||||
protected void leaked(LeakDetector.LeakInfo leakInfo)
|
||||
protected void leaked(LeakDetector<ByteBuffer>.LeakInfo leakInfo)
|
||||
{
|
||||
LOG.info("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
|
||||
LOG.warn("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@
|
|||
</dependency>
|
||||
<!-- JSTL Impl -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>org.apache.taglibs.standard.glassfish</artifactId>
|
||||
<groupId>org.glassfish.web</groupId>
|
||||
<artifactId>javax.servlet.jsp.jstl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- EL Api -->
|
||||
|
|
|
@ -125,11 +125,11 @@
|
|||
<artifactId>jetty-jsp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
<artifactId>javax.activation</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>javax.transaction-api</artifactId>
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
<!-- uncomment the following lines, changing log interval (in milliseconds) -->
|
||||
<!-- and log threshold (in percent) as desired. -->
|
||||
<!--
|
||||
<Set name="logInterval">10000</Arg>
|
||||
<Set name="logThreshold">1</Arg>
|
||||
<Set name="logInterval">10000</Set>
|
||||
<Set name="logThreshold">1</Set>
|
||||
-->
|
||||
|
||||
<!-- To enable detail dump of the server whenever a thread is detected as spinning, -->
|
||||
|
|
|
@ -7,7 +7,7 @@ server
|
|||
client
|
||||
|
||||
[lib]
|
||||
lib/jetty-monitor-${jetty.version}.jar
|
||||
lib/monitor/jetty-monitor-${jetty.version}.jar
|
||||
|
||||
[xml]
|
||||
etc/jetty-monitor.xml
|
||||
etc/jetty-monitor.xml
|
||||
|
|
|
@ -100,10 +100,10 @@
|
|||
javax.servlet.jsp;version="[2.3,2.4)",
|
||||
javax.servlet.jsp.el;version="[2.3,2.4)",
|
||||
javax.servlet.jsp.tagext;version="[2.3,2.4)",
|
||||
javax.servlet.jsp.jstl.core;version="1.2.0";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.fmt;version="1.2.0";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.sql;version="1.2.0";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.tlv;version="1.2.0";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.jasper;version="[2.3.2,2.4)";resolution:=optional,
|
||||
org.apache.jasper.compiler;version="[2.3.2,2.4)";resolution:=optional,
|
||||
org.apache.jasper.compiler.tagplugin;version="[2.3.2,2.4)";resolution:=optional,
|
||||
|
@ -114,29 +114,29 @@
|
|||
org.apache.jasper.util;version="[2.3.2,2.4)";resolution:=optional,
|
||||
org.apache.jasper.xmlparser;version="[2.3.2,2.4)";resolution:=optional,
|
||||
org.glassfish.jsp.api;version="[2.3.2,2.4)";resolution:=optional,
|
||||
org.apache.taglibs.standard;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.extra.spath;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.functions;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.parser;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.support;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.resources;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.core;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.fmt;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.sql;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.xml;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.core;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.fmt;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.sql;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.xml;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.core;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.fmt;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.sql;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.xml;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tei;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard.tlv;version="1.2.0";resolution:=optional,
|
||||
org.apache.taglibs.standard;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.resources;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
|
||||
org.osgi.*,
|
||||
org.xml.*;resolution:=optional,
|
||||
org.xml.sax.*;resolution:=optional,
|
||||
|
@ -144,7 +144,7 @@
|
|||
org.w3c.dom;resolution:=optional,
|
||||
org.w3c.dom.ls;resolution:=optional,
|
||||
javax.xml.parser;resolution:=optional
|
||||
</Import-Package>
|
||||
</Import-Package>
|
||||
<_nouses>true</_nouses>
|
||||
<DynamicImport-Package>org.apache.jasper.*;version="2.3"</DynamicImport-Package>
|
||||
</instructions>
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.eclipse.jetty.annotations.AnnotationParser.Handler;
|
|||
import org.eclipse.jetty.annotations.ClassNameResolver;
|
||||
import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
|
||||
import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
@ -37,12 +39,14 @@ import org.osgi.framework.Constants;
|
|||
*/
|
||||
public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(org.eclipse.jetty.annotations.AnnotationConfiguration.class);
|
||||
|
||||
public class BundleParserTask extends ParserTask
|
||||
{
|
||||
|
||||
public BundleParserTask (AnnotationParser parser, Set<? extends Handler>handlers, Resource resource, ClassNameResolver resolver)
|
||||
{
|
||||
super(parser, handlers, resource, resolver);
|
||||
super(parser, handlers, resource, resolver);
|
||||
}
|
||||
|
||||
public Void call() throws Exception
|
||||
|
@ -51,7 +55,11 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
{
|
||||
org.eclipse.jetty.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.osgi.annotations.AnnotationParser)_parser;
|
||||
Bundle bundle = osgiAnnotationParser.getBundle(_resource);
|
||||
if (_stat != null)
|
||||
_stat.start();
|
||||
osgiAnnotationParser.parse(_handlers, bundle, _resolver);
|
||||
if (_stat != null)
|
||||
_stat.end();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -178,7 +186,12 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot
|
|||
|
||||
ClassNameResolver classNameResolver = createClassNameResolver(context);
|
||||
if (_parserTasks != null)
|
||||
_parserTasks.add(new BundleParserTask(parser, handlers, bundleRes, classNameResolver));
|
||||
{
|
||||
BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes, classNameResolver);
|
||||
_parserTasks.add(task);
|
||||
if (LOG.isDebugEnabled())
|
||||
task.setStatistic(new TimeStatistic());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -77,6 +77,10 @@ public class TestJettyOSGiBootWebAppAsService
|
|||
options.add(CoreOptions.junitBundles());
|
||||
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
|
||||
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
|
||||
options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
|
||||
"com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
|
||||
"com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
|
||||
|
||||
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
|
||||
|
||||
String logLevel = "WARN";
|
||||
|
@ -123,7 +127,7 @@ public class TestJettyOSGiBootWebAppAsService
|
|||
//jsp bundles
|
||||
res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.apache.taglibs.standard.glassfish").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.glassfish").artifactId("javax.el").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart());
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.ops4j.pax.exam.CoreOptions;
|
|||
import org.ops4j.pax.exam.Option;
|
||||
import org.ops4j.pax.exam.junit.Configuration;
|
||||
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
|
@ -70,6 +71,10 @@ public class TestJettyOSGiBootWithJsp
|
|||
options.add(CoreOptions.junitBundles());
|
||||
options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
|
||||
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
|
||||
options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils",
|
||||
"com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
|
||||
"com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
|
||||
|
||||
options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
|
||||
|
||||
String logLevel = "WARN";
|
||||
|
@ -139,7 +144,7 @@ public class TestJettyOSGiBootWithJsp
|
|||
//jetty jsp bundles
|
||||
res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.apache.taglibs.standard.glassfish").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.glassfish").artifactId("javax.el").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject());
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart());
|
||||
|
@ -155,7 +160,7 @@ public class TestJettyOSGiBootWithJsp
|
|||
@Test
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
// at the moment can't run httpservice with jsp at the same time.
|
||||
|
@ -167,6 +172,7 @@ public class TestJettyOSGiBootWithJsp
|
|||
TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testJspDump() throws Exception
|
||||
{
|
||||
|
|
|
@ -79,13 +79,12 @@
|
|||
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
|
||||
<Set name="secureScheme">https</Set>
|
||||
<Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
|
||||
<Set name="outputBufferSize">32768</Set>
|
||||
<Set name="requestHeaderSize">8192</Set>
|
||||
<Set name="responseHeaderSize">8192</Set>
|
||||
<Set name="sendServerVersion">true</Set>
|
||||
<Set name="sendDateHeader">false</Set>
|
||||
<Set name="outputBufferSize"><Property name="jetty.output.buffer.size" default="32768" /></Set>
|
||||
<Set name="requestHeaderSize"><Property name="jetty.request.header.size" default="8192" /></Set>
|
||||
<Set name="responseHeaderSize"><Property name="jetty.response.header.size" default="8192" /></Set>
|
||||
<Set name="sendServerVersion"><Property name="jetty.send.server.version" default="true" /></Set>
|
||||
<Set name="sendDateHeader"><Property name="jetty.send.date.header" default="false" /></Set>
|
||||
<Set name="headerCacheSize">512</Set>
|
||||
|
||||
<!-- Uncomment to enable handling of X-Forwarded- style headers
|
||||
<Call name="addCustomizer">
|
||||
<Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
|
||||
|
|
|
@ -21,13 +21,25 @@ lib/jetty-io-${jetty.version}.jar
|
|||
etc/jetty.xml
|
||||
|
||||
[ini-template]
|
||||
##
|
||||
## Server Threading Configuration
|
||||
##
|
||||
# minimum number of threads
|
||||
threads.min=10
|
||||
# maximum number of threads
|
||||
threads.max=200
|
||||
# thread idle timeout in milliseconds
|
||||
threads.timeout=60000
|
||||
# buffer size for output
|
||||
jetty.output.buffer.size=32768
|
||||
# request header buffer size
|
||||
jetty.request.header.size=8192
|
||||
# response header buffer size
|
||||
jetty.response.header.size=8192
|
||||
# should jetty send the server version header?
|
||||
jetty.send.server.version=true
|
||||
# should jetty send the date header?
|
||||
jetty.send.date.header=false
|
||||
# What host to listen on (leave commented to listen on all interfaces)
|
||||
#jetty.host=myhost.com
|
||||
# Dump the state of the Jetty server, components, and webapps after startup
|
||||
|
|
|
@ -189,9 +189,10 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
|
|||
for (ConnectionFactory factory:factories)
|
||||
addConnectionFactory(factory);
|
||||
|
||||
if (acceptors<0)
|
||||
acceptors=Math.max(1,(Runtime.getRuntime().availableProcessors()) / 2);
|
||||
if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
if (acceptors < 0)
|
||||
acceptors = 1 + cores / 16;
|
||||
if (acceptors > 2 * cores)
|
||||
LOG.warn("Acceptors should be <= 2*availableProcessors: " + this);
|
||||
_acceptors = new Thread[acceptors];
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Objects;
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
|
||||
|
@ -29,38 +29,25 @@ import org.eclipse.jetty.util.log.Log;
|
|||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* <p>{@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.</p>
|
||||
* <p>{@link HttpInput} holds a queue of items passed to it by calls to {@link #content(T)}.</p>
|
||||
* <p>{@link HttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
|
||||
* but simply holds references to the item, thus the caller must organize for those buffers to valid while
|
||||
* held by this class.</p>
|
||||
* <p>To assist the caller, subclasses may override methods {@link #onContentQueued(T)},
|
||||
* {@link #onContentConsumed(T)} and {@link #onAllContentConsumed()} that can be implemented so that the
|
||||
* caller will know when buffers are queued and consumed.</p>
|
||||
*/
|
||||
/**
|
||||
* @author gregw
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
/**
|
||||
* @author gregw
|
||||
*
|
||||
* @param <T>
|
||||
* {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.
|
||||
* <p/>
|
||||
* Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class
|
||||
* maintains two states: the content state that tells whether there is content to consume and the EOF
|
||||
* state that tells whether an EOF has arrived.
|
||||
* Only once the content has been consumed the content state is moved to the EOF state.
|
||||
*/
|
||||
public abstract class HttpInput<T> extends ServletInputStream implements Runnable
|
||||
{
|
||||
private final static Logger LOG = Log.getLogger(HttpInput.class);
|
||||
|
||||
private final byte[] _oneByteBuffer = new byte[1];
|
||||
private HttpChannelState _channelState;
|
||||
private Throwable _onError;
|
||||
private ReadListener _listener;
|
||||
private boolean _notReady;
|
||||
|
||||
protected State _state = BLOCKING;
|
||||
private State _eof=null;
|
||||
private final Object _lock;
|
||||
private HttpChannelState _channelState;
|
||||
private ReadListener _listener;
|
||||
private Throwable _onError;
|
||||
private boolean _notReady;
|
||||
private State _contentState = STREAM;
|
||||
private State _eofState;
|
||||
private long _contentRead;
|
||||
|
||||
protected HttpInput()
|
||||
|
@ -70,7 +57,15 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
|
||||
protected HttpInput(Object lock)
|
||||
{
|
||||
_lock=lock==null?this:lock;
|
||||
_lock = lock == null ? this : lock;
|
||||
}
|
||||
|
||||
public void init(HttpChannelState state)
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
_channelState = state;
|
||||
}
|
||||
}
|
||||
|
||||
public final Object lock()
|
||||
|
@ -82,50 +77,15 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
_state = BLOCKING;
|
||||
_eof=null;
|
||||
_onError=null;
|
||||
_contentRead=0;
|
||||
_listener = null;
|
||||
_onError = null;
|
||||
_notReady = false;
|
||||
_contentState = STREAM;
|
||||
_eofState = null;
|
||||
_contentRead = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the next content to be consumed from. Returning the next item does not consume it
|
||||
* and it may be returned multiple times until it is consumed. Calls to {@link #get(Object, byte[], int, int)}
|
||||
* or {@link #consume(Object, int)} are required to consume data from the content.
|
||||
* @return Content or null if none available.
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract T nextContent() throws IOException;
|
||||
|
||||
/**
|
||||
* A convenience method to call nextContent and to check the return value, which if null then the
|
||||
* a check is made for EOF and the state changed accordingly.
|
||||
* @see #nextContent()
|
||||
* @return Content or null if none available.
|
||||
* @throws IOException
|
||||
*/
|
||||
protected T getNextContent() throws IOException
|
||||
{
|
||||
T content=nextContent();
|
||||
|
||||
if (content==null && _eof!=null)
|
||||
{
|
||||
LOG.debug("{} eof {}",this,_eof);
|
||||
_state=_eof;
|
||||
_eof=null;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
int read = read(_oneByteBuffer, 0, 1);
|
||||
return read < 0 ? -1 : 0xff & _oneByteBuffer[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available()
|
||||
{
|
||||
|
@ -134,7 +94,7 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
synchronized (lock())
|
||||
{
|
||||
T item = getNextContent();
|
||||
return item==null?0:remaining(item);
|
||||
return item == null ? 0 : remaining(item);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -143,46 +103,115 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
int read = read(_oneByteBuffer, 0, 1);
|
||||
return read < 0 ? -1 : _oneByteBuffer[0] & 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
T item = null;
|
||||
int l;
|
||||
synchronized (lock())
|
||||
{
|
||||
// System.err.printf("read s=%s q=%d e=%s%n",_state,_inputQ.size(),_eof);
|
||||
|
||||
// Get the current head of the input Q
|
||||
item = getNextContent();
|
||||
|
||||
// If we have no item
|
||||
T item = getNextContent();
|
||||
if (item == null)
|
||||
{
|
||||
_state.waitForContent(this);
|
||||
item=getNextContent();
|
||||
if (item==null)
|
||||
return _state.noContent();
|
||||
_contentState.waitForContent(this);
|
||||
item = getNextContent();
|
||||
if (item == null)
|
||||
return _contentState.noContent();
|
||||
}
|
||||
|
||||
l=get(item, b, off, len);
|
||||
_contentRead+=l;
|
||||
|
||||
int l = get(item, b, off, len);
|
||||
_contentRead += l;
|
||||
return l;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A convenience method to call nextContent and to check the return value, which if null then the
|
||||
* a check is made for EOF and the state changed accordingly.
|
||||
*
|
||||
* @return Content or null if none available.
|
||||
* @throws IOException
|
||||
* @see #nextContent()
|
||||
*/
|
||||
protected T getNextContent() throws IOException
|
||||
{
|
||||
T content = nextContent();
|
||||
if (content == null)
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_eofState != null)
|
||||
{
|
||||
LOG.debug("{} eof {}", this, _eofState);
|
||||
_contentState = _eofState;
|
||||
}
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the next content to be consumed from. Returning the next item does not consume it
|
||||
* and it may be returned multiple times until it is consumed.
|
||||
* <p/>
|
||||
* Calls to {@link #get(Object, byte[], int, int)}
|
||||
* or {@link #consume(Object, int)} are required to consume data from the content.
|
||||
*
|
||||
* @return the content or null if none available.
|
||||
* @throws IOException if retrieving the content fails
|
||||
*/
|
||||
protected abstract T nextContent() throws IOException;
|
||||
|
||||
/**
|
||||
* @param item the content
|
||||
* @return how many bytes remain in the given content
|
||||
*/
|
||||
protected abstract int remaining(T item);
|
||||
|
||||
/**
|
||||
* Copies the given content into the given byte buffer.
|
||||
*
|
||||
* @param item the content to copy from
|
||||
* @param buffer the buffer to copy into
|
||||
* @param offset the buffer offset to start copying from
|
||||
* @param length the space available in the buffer
|
||||
* @return the number of bytes actually copied
|
||||
*/
|
||||
protected abstract int get(T item, byte[] buffer, int offset, int length);
|
||||
|
||||
/**
|
||||
* Consumes the given content.
|
||||
*
|
||||
* @param item the content to consume
|
||||
* @param length the number of bytes to consume
|
||||
*/
|
||||
protected abstract void consume(T item, int length);
|
||||
|
||||
/**
|
||||
* Blocks until some content or some end-of-file event arrives.
|
||||
*
|
||||
* @throws IOException if the wait is interrupted
|
||||
*/
|
||||
protected abstract void blockForContent() throws IOException;
|
||||
|
||||
/**
|
||||
* Adds some content to this input stream.
|
||||
*
|
||||
* @param item the content to add
|
||||
*/
|
||||
public abstract void content(T item);
|
||||
|
||||
protected boolean onAsyncRead()
|
||||
{
|
||||
if (_listener==null)
|
||||
return false;
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_listener == null)
|
||||
return false;
|
||||
}
|
||||
_channelState.onReadPossible();
|
||||
return true;
|
||||
}
|
||||
|
@ -194,15 +223,11 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
return _contentRead;
|
||||
}
|
||||
}
|
||||
|
||||
/** Add some content to the input stream
|
||||
* @param item
|
||||
*/
|
||||
public abstract void content(T item);
|
||||
|
||||
|
||||
/** This method should be called to signal to the HttpInput
|
||||
* that an EOF has arrived before all the expected content.
|
||||
/**
|
||||
* This method should be called to signal that an EOF has been
|
||||
* detected before all the expected content arrived.
|
||||
* <p/>
|
||||
* Typically this will result in an EOFException being thrown
|
||||
* from a subsequent read rather than a -1 return.
|
||||
*/
|
||||
|
@ -210,28 +235,34 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_eof==null || !_eof.isEOF())
|
||||
if (!isEOF())
|
||||
{
|
||||
LOG.debug("{} early EOF", this);
|
||||
_eof=EARLY_EOF;
|
||||
if (_listener!=null)
|
||||
_channelState.onReadPossible();
|
||||
_eofState = EARLY_EOF;
|
||||
if (_listener == null)
|
||||
return;
|
||||
}
|
||||
}
|
||||
_channelState.onReadPossible();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be called to signal that all the expected
|
||||
* content arrived.
|
||||
*/
|
||||
public void messageComplete()
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_eof==null || !_eof.isEOF())
|
||||
if (!isEOF())
|
||||
{
|
||||
LOG.debug("{} EOF", this);
|
||||
_eof=EOF;
|
||||
if (_listener!=null)
|
||||
_channelState.onReadPossible();
|
||||
_eofState = EOF;
|
||||
if (_listener == null)
|
||||
return;
|
||||
}
|
||||
}
|
||||
_channelState.onReadPossible();
|
||||
}
|
||||
|
||||
public void consumeAll()
|
||||
|
@ -243,10 +274,10 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
while (!isFinished())
|
||||
{
|
||||
T item = getNextContent();
|
||||
if (item==null)
|
||||
_state.waitForContent(this);
|
||||
if (item == null)
|
||||
_contentState.waitForContent(this);
|
||||
else
|
||||
consume(item,remaining(item));
|
||||
consume(item, remaining(item));
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -256,35 +287,48 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether an EOF has been detected, even though there may be content to consume.
|
||||
*/
|
||||
public boolean isEOF()
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
return _eofState != null && _eofState.isEOF();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished()
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
return _state.isEOF();
|
||||
return _contentState.isEOF();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady()
|
||||
{
|
||||
boolean finished;
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_listener==null)
|
||||
if (_contentState.isEOF())
|
||||
return true;
|
||||
int available = available();
|
||||
if (available>0)
|
||||
if (_listener == null )
|
||||
return true;
|
||||
if (!_notReady)
|
||||
{
|
||||
_notReady=true;
|
||||
if (_state.isEOF())
|
||||
_channelState.onReadPossible();
|
||||
else
|
||||
unready();
|
||||
}
|
||||
return false;
|
||||
if (available() > 0)
|
||||
return true;
|
||||
if (_notReady)
|
||||
return false;
|
||||
_notReady = true;
|
||||
finished = isFinished();
|
||||
}
|
||||
if (finished)
|
||||
_channelState.onReadPossible();
|
||||
else
|
||||
unready();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void unready()
|
||||
|
@ -294,80 +338,79 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
@Override
|
||||
public void setReadListener(ReadListener readListener)
|
||||
{
|
||||
if (readListener==null)
|
||||
throw new NullPointerException("readListener==null");
|
||||
readListener = Objects.requireNonNull(readListener);
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_state!=BLOCKING)
|
||||
throw new IllegalStateException("state="+_state);
|
||||
_state=ASYNC;
|
||||
_listener=readListener;
|
||||
_notReady=true;
|
||||
|
||||
_channelState.onReadPossible();
|
||||
if (_contentState != STREAM)
|
||||
throw new IllegalStateException("state=" + _contentState);
|
||||
_contentState = ASYNC;
|
||||
_listener = readListener;
|
||||
_notReady = true;
|
||||
}
|
||||
_channelState.onReadPossible();
|
||||
}
|
||||
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
if (_onError==null)
|
||||
if (_onError == null)
|
||||
LOG.warn(x);
|
||||
else
|
||||
_onError=x;
|
||||
_onError = x;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final boolean available;
|
||||
final Throwable error;
|
||||
final ReadListener listener;
|
||||
boolean available = false;
|
||||
final boolean eof;
|
||||
final Throwable x;
|
||||
|
||||
synchronized (lock())
|
||||
{
|
||||
if (!_notReady || _listener==null)
|
||||
if (!_notReady || _listener == null)
|
||||
return;
|
||||
|
||||
x=_onError;
|
||||
T item;
|
||||
error = _onError;
|
||||
listener = _listener;
|
||||
|
||||
try
|
||||
{
|
||||
item = getNextContent();
|
||||
T item = getNextContent();
|
||||
available = item != null && remaining(item) > 0;
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
item=null;
|
||||
failed(e);
|
||||
}
|
||||
available= item!=null && remaining(item)>0;
|
||||
|
||||
eof = !available && _state.isEOF();
|
||||
_notReady=!available&&!eof;
|
||||
eof = !available && isFinished();
|
||||
_notReady = !available && !eof;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (x!=null)
|
||||
_listener.onError(x);
|
||||
if (error != null)
|
||||
listener.onError(error);
|
||||
else if (available)
|
||||
_listener.onDataAvailable();
|
||||
listener.onDataAvailable();
|
||||
else if (eof)
|
||||
_listener.onAllDataRead();
|
||||
listener.onAllDataRead();
|
||||
else
|
||||
unready();
|
||||
}
|
||||
catch(Throwable e)
|
||||
catch (Throwable e)
|
||||
{
|
||||
LOG.warn(e.toString());
|
||||
LOG.debug(e);
|
||||
_listener.onError(e);
|
||||
listener.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class State
|
||||
protected static abstract class State
|
||||
{
|
||||
public void waitForContent(HttpInput<?> in) throws IOException
|
||||
{
|
||||
|
@ -384,26 +427,28 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
}
|
||||
}
|
||||
|
||||
protected static final State BLOCKING= new State()
|
||||
protected static final State STREAM = new State()
|
||||
{
|
||||
@Override
|
||||
public void waitForContent(HttpInput<?> in) throws IOException
|
||||
public void waitForContent(HttpInput<?> input) throws IOException
|
||||
{
|
||||
in.blockForContent();
|
||||
input.blockForContent();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "OPEN";
|
||||
return "STREAM";
|
||||
}
|
||||
};
|
||||
|
||||
protected static final State ASYNC= new State()
|
||||
protected static final State ASYNC = new State()
|
||||
{
|
||||
@Override
|
||||
public int noContent() throws IOException
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
@ -411,25 +456,27 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
}
|
||||
};
|
||||
|
||||
protected static final State EARLY_EOF= new State()
|
||||
protected static final State EARLY_EOF = new State()
|
||||
{
|
||||
@Override
|
||||
public int noContent() throws IOException
|
||||
{
|
||||
throw new EofException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEOF()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "EARLY_EOF";
|
||||
}
|
||||
};
|
||||
|
||||
protected static final State EOF= new State()
|
||||
protected static final State EOF = new State()
|
||||
{
|
||||
@Override
|
||||
public boolean isEOF()
|
||||
|
@ -442,13 +489,4 @@ public abstract class HttpInput<T> extends ServletInputStream implements Runnabl
|
|||
return "EOF";
|
||||
}
|
||||
};
|
||||
|
||||
public void init(HttpChannelState state)
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
_channelState=state;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,13 +63,15 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
private volatile Throwable _onError;
|
||||
|
||||
/*
|
||||
ACTION OPEN ASYNC READY PENDING UNREADY
|
||||
-------------------------------------------------------------------------------
|
||||
setWriteListener() READY->owp ise ise ise ise
|
||||
write() OPEN ise PENDING wpe wpe
|
||||
flush() OPEN ise PENDING wpe wpe
|
||||
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false
|
||||
write completed - - - ASYNC READY->owp
|
||||
ACTION OPEN ASYNC READY PENDING UNREADY CLOSED
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
setWriteListener() READY->owp ise ise ise ise ise
|
||||
write() OPEN ise PENDING wpe wpe eof
|
||||
flush() OPEN ise PENDING wpe wpe eof
|
||||
close() CLOSED CLOSED CLOSED CLOSED wpe CLOSED
|
||||
isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false CLOSED:true
|
||||
write completed - - - ASYNC READY->owp -
|
||||
|
||||
*/
|
||||
enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, CLOSED }
|
||||
private final AtomicReference<OutputState> _state=new AtomicReference<>(OutputState.OPEN);
|
||||
|
@ -131,48 +133,66 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
@Override
|
||||
public void close()
|
||||
{
|
||||
OutputState state=_state.get();
|
||||
while(state!=OutputState.CLOSED)
|
||||
loop: while(true)
|
||||
{
|
||||
if (_state.compareAndSet(state,OutputState.CLOSED))
|
||||
OutputState state=_state.get();
|
||||
switch (state)
|
||||
{
|
||||
try
|
||||
{
|
||||
write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
_channel.failed();
|
||||
}
|
||||
releaseBuffer();
|
||||
return;
|
||||
case CLOSED:
|
||||
break loop;
|
||||
|
||||
case UNREADY:
|
||||
throw new WritePendingException(); // TODO ?
|
||||
|
||||
default:
|
||||
if (_state.compareAndSet(state,OutputState.CLOSED))
|
||||
{
|
||||
try
|
||||
{
|
||||
write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
_channel.failed();
|
||||
}
|
||||
releaseBuffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
state=_state.get();
|
||||
}
|
||||
}
|
||||
|
||||
/* Called to indicated that the output is already closed and the state needs to be updated to match */
|
||||
void closed()
|
||||
{
|
||||
OutputState state=_state.get();
|
||||
while(state!=OutputState.CLOSED)
|
||||
loop: while(true)
|
||||
{
|
||||
if (_state.compareAndSet(state,OutputState.CLOSED))
|
||||
OutputState state=_state.get();
|
||||
switch (state)
|
||||
{
|
||||
try
|
||||
{
|
||||
_channel.getResponse().closeOutput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
_channel.failed();
|
||||
}
|
||||
releaseBuffer();
|
||||
return;
|
||||
case CLOSED:
|
||||
break loop;
|
||||
|
||||
case UNREADY:
|
||||
throw new WritePendingException(); // TODO ?
|
||||
|
||||
default:
|
||||
if (_state.compareAndSet(state,OutputState.CLOSED))
|
||||
{
|
||||
try
|
||||
{
|
||||
_channel.getResponse().closeOutput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
_channel.failed();
|
||||
}
|
||||
releaseBuffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
state=_state.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -667,8 +687,9 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
return false;
|
||||
case UNREADY:
|
||||
return false;
|
||||
|
||||
case CLOSED:
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -680,23 +701,49 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
Throwable th=_onError;
|
||||
_onError=null;
|
||||
_writeListener.onError(th);
|
||||
_writeListener.onError(new IOException(th));
|
||||
close();
|
||||
}
|
||||
if (_state.get()==OutputState.READY)
|
||||
|
||||
switch(_state.get())
|
||||
{
|
||||
try
|
||||
{
|
||||
_writeListener.onWritePossible();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
_writeListener.onError(e);
|
||||
close();
|
||||
}
|
||||
case READY:
|
||||
try
|
||||
{
|
||||
_writeListener.onWritePossible();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
_writeListener.onError(e);
|
||||
close();
|
||||
}
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
try
|
||||
{
|
||||
new Throwable().printStackTrace();
|
||||
// even though a write is not possible, because a close has
|
||||
// occurred, we need to call onWritePossible to tell async
|
||||
// producer that the last write completed.
|
||||
_writeListener.onWritePossible();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
_writeListener.onError(e);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),_state.get());
|
||||
}
|
||||
|
||||
private abstract class AsyncICB extends IteratingCallback
|
||||
{
|
||||
|
@ -722,7 +769,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
break;
|
||||
|
||||
case CLOSED:
|
||||
_onError=new EofException("Closed");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -21,44 +21,58 @@ package org.eclipse.jetty.server;
|
|||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
|
||||
import org.eclipse.jetty.util.ArrayQueue;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* <p>{@link QueuedHttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.</p>
|
||||
* <p>{@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.</p>
|
||||
* <p>{@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
|
||||
* {@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.
|
||||
* <p/>
|
||||
* {@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them
|
||||
* but simply holds references to the item, thus the caller must organize for those buffers to valid while
|
||||
* held by this class.</p>
|
||||
* <p>To assist the caller, subclasses may override methods {@link #onAsyncRead()},
|
||||
* {@link #onContentConsumed(Object)} and {@link #onAllContentConsumed()} that can be implemented so that the
|
||||
* caller will know when buffers are queued and consumed.</p>
|
||||
* held by this class.
|
||||
* <p/>
|
||||
* To assist the caller, subclasses may override methods {@link #onAsyncRead()}, {@link #onContentConsumed(Object)}
|
||||
* that can be implemented so that the caller will know when buffers are queued and consumed.
|
||||
*/
|
||||
public abstract class QueuedHttpInput<T> extends HttpInput<T>
|
||||
{
|
||||
private final static Logger LOG = Log.getLogger(QueuedHttpInput.class);
|
||||
|
||||
private final ArrayQueue<T> _inputQ = new ArrayQueue<>(lock());
|
||||
|
||||
|
||||
public QueuedHttpInput()
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
public void content(T item)
|
||||
{
|
||||
// The buffer is not copied here. This relies on the caller not recycling the buffer
|
||||
// until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are
|
||||
// the signals to the caller that the buffers can be recycled.
|
||||
|
||||
synchronized (lock())
|
||||
{
|
||||
boolean wasEmpty = _inputQ.isEmpty();
|
||||
_inputQ.add(item);
|
||||
LOG.debug("{} queued {}", this, item);
|
||||
if (wasEmpty)
|
||||
{
|
||||
if (!onAsyncRead())
|
||||
lock().notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void recycle()
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
T item = _inputQ.peekUnsafe();
|
||||
T item = _inputQ.pollUnsafe();
|
||||
while (item != null)
|
||||
{
|
||||
_inputQ.pollUnsafe();
|
||||
onContentConsumed(item);
|
||||
|
||||
item = _inputQ.peekUnsafe();
|
||||
if (item == null)
|
||||
onAllContentConsumed();
|
||||
item = _inputQ.pollUnsafe();
|
||||
}
|
||||
super.recycle();
|
||||
}
|
||||
|
@ -67,30 +81,27 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
|
|||
@Override
|
||||
protected T nextContent()
|
||||
{
|
||||
T item = _inputQ.peekUnsafe();
|
||||
|
||||
// Skip empty items at the head of the queue
|
||||
while (item != null && remaining(item) == 0)
|
||||
synchronized (lock())
|
||||
{
|
||||
_inputQ.pollUnsafe();
|
||||
onContentConsumed(item);
|
||||
LOG.debug("{} consumed {}", this, item);
|
||||
item = _inputQ.peekUnsafe();
|
||||
|
||||
// If that was the last item then notify
|
||||
if (item==null)
|
||||
onAllContentConsumed();
|
||||
// Items are removed only when they are fully consumed.
|
||||
T item = _inputQ.peekUnsafe();
|
||||
// Skip consumed items at the head of the queue.
|
||||
while (item != null && remaining(item) == 0)
|
||||
{
|
||||
_inputQ.pollUnsafe();
|
||||
onContentConsumed(item);
|
||||
LOG.debug("{} consumed {}", this, item);
|
||||
item = _inputQ.peekUnsafe();
|
||||
}
|
||||
return item;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
protected abstract void onContentConsumed(T item);
|
||||
|
||||
protected void blockForContent() throws IOException
|
||||
{
|
||||
synchronized (lock())
|
||||
{
|
||||
while (_inputQ.isEmpty() && !_state.isEOF())
|
||||
while (_inputQ.isEmpty() && !isFinished() && !isEOF())
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -105,40 +116,12 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Called by this HttpInput to signal all available content has been consumed
|
||||
/**
|
||||
* Callback that signals that the given content has been consumed.
|
||||
*
|
||||
* @param item the consumed content
|
||||
*/
|
||||
protected void onAllContentConsumed()
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add some content to the input stream
|
||||
* @param item
|
||||
*/
|
||||
public void content(T item)
|
||||
{
|
||||
// The buffer is not copied here. This relies on the caller not recycling the buffer
|
||||
// until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are
|
||||
// the signals to the caller that the buffers can be recycled.
|
||||
|
||||
synchronized (lock())
|
||||
{
|
||||
boolean empty=_inputQ.isEmpty();
|
||||
|
||||
_inputQ.add(item);
|
||||
|
||||
if (empty)
|
||||
{
|
||||
if (!onAsyncRead())
|
||||
lock().notify();
|
||||
}
|
||||
|
||||
LOG.debug("{} queued {}", this, item);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void onContentConsumed(T item);
|
||||
|
||||
public void earlyEOF()
|
||||
{
|
||||
|
@ -148,7 +131,7 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
|
|||
lock().notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void messageComplete()
|
||||
{
|
||||
synchronized (lock())
|
||||
|
@ -157,5 +140,4 @@ public abstract class QueuedHttpInput<T> extends HttpInput<T>
|
|||
lock().notify();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ public class ServerConnector extends AbstractNetworkConnector
|
|||
@Name("factories") ConnectionFactory... factories)
|
||||
{
|
||||
super(server,executor,scheduler,bufferPool,acceptors,factories);
|
||||
_manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors >= 0 ? selectors : Runtime.getRuntime().availableProcessors());
|
||||
_manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors > 0 ? selectors : Runtime.getRuntime().availableProcessors());
|
||||
addBean(_manager, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.server.handler;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
@ -2258,11 +2260,46 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader()
|
||||
{
|
||||
AccessController.checkPermission(new RuntimePermission("getClassLoader"));
|
||||
return _classLoader;
|
||||
@Override
|
||||
public ClassLoader getClassLoader()
|
||||
{
|
||||
if (!_enabled)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
//no security manager just return the classloader
|
||||
if (System.getSecurityManager() == null)
|
||||
return _classLoader;
|
||||
else
|
||||
{
|
||||
//check to see if the classloader of the caller is the same as the context
|
||||
//classloader, or a parent of it
|
||||
try
|
||||
{
|
||||
Class reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
|
||||
Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
|
||||
Class caller = (Class)getCallerClass.invoke(null, 2);
|
||||
|
||||
boolean ok = false;
|
||||
ClassLoader callerLoader = caller.getClassLoader();
|
||||
while (!ok && callerLoader != null)
|
||||
{
|
||||
if (callerLoader == _classLoader)
|
||||
ok = true;
|
||||
else
|
||||
callerLoader = callerLoader.getParent();
|
||||
}
|
||||
|
||||
if (ok)
|
||||
return _classLoader;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to check classloader of caller",e);
|
||||
}
|
||||
|
||||
AccessController.checkPermission(new RuntimePermission("getClassLoader"));
|
||||
return _classLoader;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,83 +18,43 @@
|
|||
|
||||
package org.eclipse.jetty.server.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.NetworkTrafficListener;
|
||||
import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.SelectorManager;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NetworkTrafficServerConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
||||
/**
|
||||
* <p>A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.</p>
|
||||
* <p>{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has
|
||||
* been started without causing {@link ConcurrentModificationException}s.</p>
|
||||
* @deprecated use {@link org.eclipse.jetty.server.NetworkTrafficServerConnector} instead.
|
||||
*/
|
||||
public class NetworkTrafficSelectChannelConnector extends ServerConnector
|
||||
@Deprecated
|
||||
public class NetworkTrafficSelectChannelConnector extends NetworkTrafficServerConnector
|
||||
{
|
||||
private final List<NetworkTrafficListener> listeners = new CopyOnWriteArrayList<NetworkTrafficListener>();
|
||||
|
||||
public NetworkTrafficSelectChannelConnector(Server server)
|
||||
{
|
||||
this(server,null,null,null,0,0,new HttpConnectionFactory());
|
||||
super(server);
|
||||
}
|
||||
|
||||
public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory)
|
||||
{
|
||||
super(server,sslContextFactory,connectionFactory);
|
||||
super(server, connectionFactory, sslContextFactory);
|
||||
}
|
||||
|
||||
public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory)
|
||||
{
|
||||
super(server,connectionFactory);
|
||||
super(server, connectionFactory);
|
||||
}
|
||||
|
||||
public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors,
|
||||
ConnectionFactory... factories)
|
||||
public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories)
|
||||
{
|
||||
super(server,executor,scheduler,pool,acceptors,selectors,factories);
|
||||
super(server, executor, scheduler, pool, acceptors, selectors, factories);
|
||||
}
|
||||
|
||||
public NetworkTrafficSelectChannelConnector(Server server, SslContextFactory sslContextFactory)
|
||||
{
|
||||
super(server,sslContextFactory);
|
||||
super(server, sslContextFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void addNetworkTrafficListener(NetworkTrafficListener listener)
|
||||
{
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param listener the listener to remove
|
||||
*/
|
||||
public void removeNetworkTrafficListener(NetworkTrafficListener listener)
|
||||
{
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key) throws IOException
|
||||
{
|
||||
NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
|
||||
endPoint.notifyOpened();
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,6 +50,12 @@ public class HashedSession extends AbstractSession
|
|||
* due to serialization failures that are most likely caused by user
|
||||
* data stored in the session that is not serializable. */
|
||||
private transient boolean _saveFailed = false;
|
||||
|
||||
/**
|
||||
* True if an attempt has been made to de-idle a session and it failed. Once
|
||||
* true, the session will not be attempted to be de-idled again.
|
||||
*/
|
||||
private transient boolean _deIdleFailed = false;
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request)
|
||||
|
@ -68,7 +74,7 @@ public class HashedSession extends AbstractSession
|
|||
/* ------------------------------------------------------------- */
|
||||
protected void checkValid()
|
||||
{
|
||||
if (_hashSessionManager._idleSavePeriodMs!=0)
|
||||
if (!_deIdleFailed && _hashSessionManager._idleSavePeriodMs!=0)
|
||||
deIdle();
|
||||
super.checkValid();
|
||||
}
|
||||
|
@ -196,7 +202,7 @@ public class HashedSession extends AbstractSession
|
|||
/* ------------------------------------------------------------ */
|
||||
public synchronized void deIdle()
|
||||
{
|
||||
if (isIdled())
|
||||
if (isIdled() && !_deIdleFailed)
|
||||
{
|
||||
// Access now to prevent race with idling period
|
||||
access(System.currentTimeMillis());
|
||||
|
@ -225,6 +231,7 @@ public class HashedSession extends AbstractSession
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
deIdleFailed();
|
||||
LOG.warn("Problem de-idling session " + super.getId(), e);
|
||||
if (fis != null) IO.close(fis);//Must ensure closed before invalidate
|
||||
invalidate();
|
||||
|
@ -265,4 +272,15 @@ public class HashedSession extends AbstractSession
|
|||
_saveFailed = true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public synchronized void deIdleFailed()
|
||||
{
|
||||
_deIdleFailed = true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public synchronized boolean isDeIdleFailed()
|
||||
{
|
||||
return _deIdleFailed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -299,7 +299,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager
|
|||
if (_dbAdaptor == null)
|
||||
throw new IllegalStateException ("No DBAdaptor");
|
||||
String longType = _dbAdaptor.getLongType();
|
||||
return "alter table "+getTableName()+" add "+longType+" not null default "+MAX_INTERVAL_NOT_SET;
|
||||
return "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType+" not null default "+MAX_INTERVAL_NOT_SET;
|
||||
}
|
||||
|
||||
private void checkNotNull(String s)
|
||||
|
|
|
@ -95,49 +95,49 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
/**
|
||||
* If dirty, session needs to be (re)persisted
|
||||
*/
|
||||
private boolean _dirty=false;
|
||||
protected boolean _dirty=false;
|
||||
|
||||
|
||||
/**
|
||||
* Time in msec since the epoch that a session cookie was set for this session
|
||||
*/
|
||||
private long _cookieSet;
|
||||
protected long _cookieSet;
|
||||
|
||||
|
||||
/**
|
||||
* Time in msec since the epoch that the session will expire
|
||||
*/
|
||||
private long _expiryTime;
|
||||
protected long _expiryTime;
|
||||
|
||||
|
||||
/**
|
||||
* Time in msec since the epoch that the session was last persisted
|
||||
*/
|
||||
private long _lastSaved;
|
||||
protected long _lastSaved;
|
||||
|
||||
|
||||
/**
|
||||
* Unique identifier of the last node to host the session
|
||||
*/
|
||||
private String _lastNode;
|
||||
protected String _lastNode;
|
||||
|
||||
|
||||
/**
|
||||
* Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
|
||||
*/
|
||||
private String _virtualHost;
|
||||
protected String _virtualHost;
|
||||
|
||||
|
||||
/**
|
||||
* Unique row in db for session
|
||||
*/
|
||||
private String _rowId;
|
||||
protected String _rowId;
|
||||
|
||||
|
||||
/**
|
||||
* Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
|
||||
*/
|
||||
private String _canonicalContext;
|
||||
protected String _canonicalContext;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -246,7 +246,8 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
public void setAttribute (String name, Object value)
|
||||
{
|
||||
_dirty = (updateAttribute(name, value) || _dirty);
|
||||
updateAttribute(name, value);
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -494,6 +495,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
" interval="+(_saveIntervalSec * 1000L));
|
||||
else
|
||||
LOG.debug("getSession("+idInCluster+"): in session map, "+
|
||||
" hashcode="+memSession.hashCode()+
|
||||
" now="+now+
|
||||
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
|
||||
" interval="+(_saveIntervalSec * 1000L)+
|
||||
|
@ -565,7 +567,11 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
|
||||
}
|
||||
else
|
||||
{
|
||||
//the session loaded from the db and the one in memory are the same, so keep using the one in memory
|
||||
session = memSession;
|
||||
LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -769,6 +775,20 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
{
|
||||
return new Session(request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param sessionId
|
||||
* @param rowId
|
||||
* @param created
|
||||
* @param accessed
|
||||
* @param maxInterval
|
||||
* @return
|
||||
*/
|
||||
protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval)
|
||||
{
|
||||
return new Session(sessionId, rowId, created, accessed, maxInterval);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Remove session from manager
|
||||
|
@ -892,7 +912,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
{
|
||||
maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager
|
||||
}
|
||||
session = new Session(id, result.getString(_sessionTableSchema.getRowIdColumn()),
|
||||
session = (Session)newSession(id, result.getString(_sessionTableSchema.getRowIdColumn()),
|
||||
result.getLong(_sessionTableSchema.getCreateTimeColumn()),
|
||||
result.getLong(_sessionTableSchema.getAccessTimeColumn()),
|
||||
maxInterval);
|
||||
|
@ -963,7 +983,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
long now = System.currentTimeMillis();
|
||||
connection.setAutoCommit(true);
|
||||
statement.setString(1, rowId); //rowId
|
||||
statement.setString(2, session.getId()); //session id
|
||||
statement.setString(2, session.getClusterId()); //session id
|
||||
statement.setString(3, session.getCanonicalContext()); //context path
|
||||
statement.setString(4, session.getVirtualHost()); //first vhost
|
||||
statement.setString(5, getSessionIdManager().getWorkerName());//my node id
|
||||
|
@ -1011,7 +1031,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
{
|
||||
long now = System.currentTimeMillis();
|
||||
connection.setAutoCommit(true);
|
||||
statement.setString(1, data.getId());
|
||||
statement.setString(1, data.getClusterId());
|
||||
statement.setString(2, getSessionIdManager().getWorkerName());//my node id
|
||||
statement.setLong(3, data.getAccessed());//accessTime
|
||||
statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime
|
||||
|
@ -1085,7 +1105,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
data.setLastSaved(now);
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Updated access time session id="+data.getId());
|
||||
LOG.debug("Updated access time session id="+data.getId()+" with lastsaved="+data.getLastSaved());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -348,7 +348,7 @@ public class HttpOutputTest
|
|||
assertThat(response,containsString("Content-Length"));
|
||||
assertThat(response,containsString("400\tThis is a big file"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteLargeKnown() throws Exception
|
||||
{
|
||||
|
@ -546,6 +546,22 @@ public class HttpOutputTest
|
|||
assertThat(response,Matchers.not(containsString("Content-Length")));
|
||||
assertThat(response,containsString("400\tThis is a big file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsyncWriteSimpleKnown() throws Exception
|
||||
{
|
||||
final Resource big = Resource.newClassPathResource("simple/simple.txt");
|
||||
|
||||
_handler._async=true;
|
||||
_handler._writeLengthIfKnown=true;
|
||||
_handler._content=BufferUtil.toBuffer(big,false);
|
||||
_handler._arrayBuffer=new byte[4000];
|
||||
|
||||
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
|
||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response,containsString("Content-Length: 11"));
|
||||
assertThat(response,containsString("simple text"));
|
||||
}
|
||||
|
||||
static class ContentHandler extends AbstractHandler
|
||||
{
|
||||
|
@ -664,7 +680,6 @@ public class HttpOutputTest
|
|||
BufferUtil.flipToFlush(_byteBuffer,0);
|
||||
out.write(_byteBuffer);
|
||||
}
|
||||
Assert.assertFalse(out.isReady());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -31,7 +28,6 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -39,25 +35,27 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.io.NetworkTrafficListener;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.junit.After;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@Ignore
|
||||
public class NetworkTrafficListenerTest
|
||||
{
|
||||
private static final byte END_OF_CONTENT = '~';
|
||||
|
||||
private Server server;
|
||||
private NetworkTrafficSelectChannelConnector connector;
|
||||
private NetworkTrafficServerConnector connector;
|
||||
|
||||
public void initConnector(Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
|
||||
connector = new NetworkTrafficSelectChannelConnector(server);
|
||||
connector = new NetworkTrafficServerConnector(server);
|
||||
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
|
||||
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
|
||||
server.addConnector(connector);
|
||||
|
@ -121,9 +119,9 @@ public class NetworkTrafficListenerTest
|
|||
}
|
||||
});
|
||||
|
||||
final AtomicReference<String> incomingData = new AtomicReference<String>();
|
||||
final AtomicReference<String> incomingData = new AtomicReference<>();
|
||||
final CountDownLatch incomingLatch = new CountDownLatch(1);
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<>("");
|
||||
final CountDownLatch outgoingLatch = new CountDownLatch(1);
|
||||
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
|
||||
{
|
||||
|
@ -186,9 +184,9 @@ public class NetworkTrafficListenerTest
|
|||
}
|
||||
});
|
||||
|
||||
final AtomicReference<String> incomingData = new AtomicReference<String>();
|
||||
final AtomicReference<String> incomingData = new AtomicReference<>();
|
||||
final CountDownLatch incomingLatch = new CountDownLatch(1);
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<>("");
|
||||
final CountDownLatch outgoingLatch = new CountDownLatch(2);
|
||||
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
|
||||
{
|
||||
|
@ -253,9 +251,9 @@ public class NetworkTrafficListenerTest
|
|||
}
|
||||
});
|
||||
|
||||
final AtomicReference<String> incomingData = new AtomicReference<String>();
|
||||
final AtomicReference<String> incomingData = new AtomicReference<>();
|
||||
final CountDownLatch incomingLatch = new CountDownLatch(1);
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<>("");
|
||||
final CountDownLatch outgoingLatch = new CountDownLatch(4);
|
||||
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
|
||||
{
|
||||
|
@ -319,9 +317,9 @@ public class NetworkTrafficListenerTest
|
|||
}
|
||||
});
|
||||
|
||||
final AtomicReference<String> incomingData = new AtomicReference<String>();
|
||||
final AtomicReference<String> incomingData = new AtomicReference<>();
|
||||
final CountDownLatch incomingLatch = new CountDownLatch(1);
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<>("");
|
||||
final CountDownLatch outgoingLatch = new CountDownLatch(1);
|
||||
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
|
||||
{
|
||||
|
@ -393,8 +391,8 @@ public class NetworkTrafficListenerTest
|
|||
}
|
||||
});
|
||||
|
||||
final AtomicReference<String> incomingData = new AtomicReference<String>("");
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<String>("");
|
||||
final AtomicReference<String> incomingData = new AtomicReference<>("");
|
||||
final AtomicReference<String> outgoingData = new AtomicReference<>("");
|
||||
final CountDownLatch outgoingLatch = new CountDownLatch(1);
|
||||
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -319,7 +319,7 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
IdentityService getIdentityService()
|
||||
protected IdentityService getIdentityService()
|
||||
{
|
||||
return _identityService;
|
||||
}
|
||||
|
@ -647,7 +647,7 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
|
||||
protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
|
||||
{
|
||||
String key=pathInContext==null?servletHolder.getName():pathInContext;
|
||||
int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
|
||||
|
@ -735,7 +735,7 @@ public class ServletHandler extends ScopedHandler
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void invalidateChainsCache()
|
||||
protected void invalidateChainsCache()
|
||||
{
|
||||
if (_chainLRU[FilterMapping.REQUEST]!=null)
|
||||
{
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
@ -28,14 +26,18 @@ import java.net.Socket;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -52,12 +54,14 @@ import org.junit.Assert;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
// TODO need these on SPDY as well!
|
||||
public class AsyncServletIOTest
|
||||
{
|
||||
protected AsyncIOServlet _servlet=new AsyncIOServlet();
|
||||
protected AsyncIOServlet _servlet0=new AsyncIOServlet();
|
||||
protected AsyncIOServlet2 _servlet2=new AsyncIOServlet2();
|
||||
protected int _port;
|
||||
|
||||
protected Server _server = new Server();
|
||||
protected ServletHandler _servletHandler;
|
||||
protected ServerConnector _connector;
|
||||
|
@ -70,13 +74,20 @@ public class AsyncServletIOTest
|
|||
_connector = new ServerConnector(_server,new HttpConnectionFactory(http_config));
|
||||
|
||||
_server.setConnectors(new Connector[]{ _connector });
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/ctx");
|
||||
_server.setHandler(context);
|
||||
_servletHandler=context.getServletHandler();
|
||||
ServletHolder holder=new ServletHolder(_servlet);
|
||||
|
||||
|
||||
ServletHolder holder=new ServletHolder(_servlet0);
|
||||
holder.setAsyncSupported(true);
|
||||
_servletHandler.addServletWithMapping(holder,"/path/*");
|
||||
|
||||
ServletHolder holder2=new ServletHolder(_servlet2);
|
||||
holder.setAsyncSupported(true);
|
||||
_servletHandler.addServletWithMapping(holder2,"/path2/*");
|
||||
|
||||
_server.start();
|
||||
_port=_connector.getLocalPort();
|
||||
|
||||
|
@ -145,16 +156,53 @@ public class AsyncServletIOTest
|
|||
{
|
||||
process("Hello!!!\r\n",10);
|
||||
}
|
||||
|
||||
|
||||
protected void assertContains(String content,String response)
|
||||
|
||||
@Test
|
||||
public void testAsync2() throws Exception
|
||||
{
|
||||
Assert.assertThat(response,Matchers.containsString(content));
|
||||
}
|
||||
|
||||
protected void assertNotContains(String content,String response)
|
||||
{
|
||||
Assert.assertThat(response,Matchers.not(Matchers.containsString(content)));
|
||||
StringBuilder request = new StringBuilder(512);
|
||||
request.append("GET /ctx/path2/info HTTP/1.1\r\n")
|
||||
.append("Host: localhost\r\n")
|
||||
.append("Connection: close\r\n")
|
||||
.append("\r\n");
|
||||
|
||||
int port=_port;
|
||||
List<String> list = new ArrayList<>();
|
||||
try (Socket socket = new Socket("localhost",port))
|
||||
{
|
||||
socket.setSoTimeout(1000000);
|
||||
OutputStream out = socket.getOutputStream();
|
||||
out.write(request.toString().getBytes("ISO-8859-1"));
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()),102400);
|
||||
|
||||
// response line
|
||||
String line = in.readLine();
|
||||
// System.err.println("resp: "+line);
|
||||
Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
|
||||
|
||||
// Skip headers
|
||||
while (line!=null)
|
||||
{
|
||||
line = in.readLine();
|
||||
// System.err.println("line: "+line);
|
||||
if (line.length()==0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Get body slowly
|
||||
while (true)
|
||||
{
|
||||
line = in.readLine();
|
||||
// System.err.println("body: "+line);
|
||||
if (line==null)
|
||||
break;
|
||||
list.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(list.get(0),"data");
|
||||
Assert.assertTrue(_servlet2.completed.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public synchronized List<String> process(String content,int... writes) throws Exception
|
||||
|
@ -183,14 +231,14 @@ public class AsyncServletIOTest
|
|||
.append("Connection: close\r\n");
|
||||
|
||||
if (content!=null)
|
||||
request.append("Content-Length: "+content.length+"\r\n")
|
||||
request.append("Content-Length: ").append(content.length).append("\r\n")
|
||||
.append("Content-Type: text/plain\r\n");
|
||||
|
||||
request.append("\r\n");
|
||||
|
||||
int port=_port;
|
||||
List<String> list = new ArrayList<>();
|
||||
try (Socket socket = new Socket("localhost",port);)
|
||||
try (Socket socket = new Socket("localhost",port))
|
||||
{
|
||||
socket.setSoTimeout(1000000);
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
@ -266,9 +314,9 @@ public class AsyncServletIOTest
|
|||
private static final long serialVersionUID = -8161977157098646562L;
|
||||
|
||||
public AsyncIOServlet()
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
|
@ -298,7 +346,7 @@ public class AsyncServletIOTest
|
|||
throw new IllegalStateException();
|
||||
|
||||
// System.err.println("ODA");
|
||||
while (in.isReady())
|
||||
while (in.isReady() && !in.isFinished())
|
||||
{
|
||||
_oda.incrementAndGet();
|
||||
int len=in.read(_buf);
|
||||
|
@ -374,4 +422,80 @@ public class AsyncServletIOTest
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class AsyncIOServlet2 extends HttpServlet
|
||||
{
|
||||
public CountDownLatch completed = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException
|
||||
{
|
||||
new SampleAsycListener(request,response);
|
||||
}
|
||||
|
||||
class SampleAsycListener implements WriteListener, AsyncListener
|
||||
{
|
||||
final ServletResponse response;
|
||||
final ServletOutputStream servletOutputStream;
|
||||
final AsyncContext asyncContext;
|
||||
volatile boolean written=false;
|
||||
|
||||
SampleAsycListener(HttpServletRequest request,HttpServletResponse response) throws IOException
|
||||
{
|
||||
asyncContext = request.startAsync();
|
||||
asyncContext.setTimeout(10000L);
|
||||
asyncContext.addListener(this);
|
||||
servletOutputStream = response.getOutputStream();
|
||||
servletOutputStream.setWriteListener(this);
|
||||
this.response=response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWritePossible() throws IOException
|
||||
{
|
||||
if (!written)
|
||||
{
|
||||
written=true;
|
||||
response.setContentLength(5);
|
||||
servletOutputStream.write("data\n".getBytes());
|
||||
}
|
||||
|
||||
if (servletOutputStream.isReady())
|
||||
{
|
||||
asyncContext.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
asyncContext.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(final AsyncEvent event) throws IOException
|
||||
{
|
||||
completed.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(final AsyncEvent event) throws IOException
|
||||
{
|
||||
asyncContext.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final AsyncEvent event) throws IOException
|
||||
{
|
||||
asyncContext.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -392,6 +392,13 @@ public class SPDYClient
|
|||
return Collections.unmodifiableCollection(sessions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dumpThis(Appendable out) throws IOException
|
||||
{
|
||||
super.dumpThis(out);
|
||||
dump(out, "", sessions);
|
||||
}
|
||||
|
||||
private class ClientSelectorManager extends SelectorManager
|
||||
{
|
||||
private ClientSelectorManager(Executor executor, Scheduler scheduler)
|
||||
|
|
|
@ -180,6 +180,7 @@ public class Flusher
|
|||
// Has the stream been reset for this data frame ?
|
||||
if (stream != null && stream.isReset() && frameBytes instanceof StandardSession.DataFrameBytes)
|
||||
{
|
||||
// TODO: notify from within sync block !
|
||||
frameBytes.failed(new StreamException(frameBytes.getStream().getId(),
|
||||
StreamStatus.INVALID_STREAM, "Stream: " + frameBytes.getStream() + " is reset!"));
|
||||
continue;
|
||||
|
|
|
@ -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
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Queue;
|
||||
|
@ -182,6 +183,13 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory
|
|||
return Collections.unmodifiableCollection(sessions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dumpThis(Appendable out) throws IOException
|
||||
{
|
||||
super.dumpThis(out);
|
||||
dump(out, "", sessions);
|
||||
}
|
||||
|
||||
private class ServerSPDYConnection extends SPDYConnection implements Runnable
|
||||
{
|
||||
private final ServerSessionFrameListener listener;
|
||||
|
|
|
@ -57,12 +57,12 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SynDataReplyDataLoadTest extends AbstractTest
|
||||
{
|
||||
private static final int TIMEOUT = 60000;
|
||||
private static final int TIMEOUT = 60 * 1000;
|
||||
private static final Logger logger = Log.getLogger(SynDataReplyDataLoadTest.class);
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
|
@ -104,14 +104,20 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
};
|
||||
}
|
||||
};
|
||||
|
||||
short spdyVersion = SPDY.V2;
|
||||
long idleTimeout = 2 * TIMEOUT;
|
||||
|
||||
server = newServer();
|
||||
connector = new ServerConnector(server, null, null, serverBufferPool, 1,
|
||||
Runtime.getRuntime().availableProcessors() / 2, new SPDYServerConnectionFactory(SPDY.V3, listener));
|
||||
Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
|
||||
new SPDYServerConnectionFactory(spdyVersion, listener));
|
||||
connector.setIdleTimeout(idleTimeout);
|
||||
|
||||
QueuedThreadPool clientExecutor = new QueuedThreadPool();
|
||||
clientExecutor.setName(clientExecutor.getName() + "-client");
|
||||
clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, 30000);
|
||||
final Session session = startClient(SPDY.V3, startServer(SPDY.V3, listener), null);
|
||||
clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, idleTimeout);
|
||||
final Session session = startClient(spdyVersion, startServer(spdyVersion, listener), null);
|
||||
|
||||
final Thread testThread = Thread.currentThread();
|
||||
Runnable timeout = new Runnable()
|
||||
|
@ -162,7 +168,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
}
|
||||
});
|
||||
}
|
||||
Scheduler.Task timeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
|
||||
Scheduler.Task syncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
|
||||
{
|
||||
long begin = System.nanoTime();
|
||||
List<Future<Object>> futures = threadPool.invokeAll(tasks);
|
||||
|
@ -172,7 +178,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
long end = System.nanoTime();
|
||||
System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
|
||||
}
|
||||
timeoutTask.cancel();
|
||||
syncTimeoutTask.cancel();
|
||||
|
||||
tasks.clear();
|
||||
for (int i = 0; i < count; ++i)
|
||||
|
@ -187,7 +193,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
}
|
||||
});
|
||||
}
|
||||
timeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
|
||||
Scheduler.Task asyncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS);
|
||||
{
|
||||
long begin = System.nanoTime();
|
||||
List<Future<Object>> futures = threadPool.invokeAll(tasks);
|
||||
|
@ -197,7 +203,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
long end = System.nanoTime();
|
||||
System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin));
|
||||
}
|
||||
timeoutTask.cancel();
|
||||
asyncTimeoutTask.cancel();
|
||||
|
||||
threadPool.shutdown();
|
||||
|
||||
Assert.assertEquals(0, leaks.get());
|
||||
|
@ -206,7 +213,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
private void synCompletedData(Session session, Fields headers, int iterations) throws Exception
|
||||
{
|
||||
final Map<Integer, Integer> counter = new ConcurrentHashMap<>(iterations);
|
||||
final CountDownLatch latch = new CountDownLatch(2 * iterations);
|
||||
final CountDownLatch requestsLatch = new CountDownLatch(2 * iterations);
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
{
|
||||
final AtomicInteger count = new AtomicInteger(2);
|
||||
|
@ -218,7 +225,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertEquals(2, count.getAndDecrement());
|
||||
latch.countDown();
|
||||
requestsLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -230,7 +237,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
{
|
||||
Assert.assertEquals(1, count.getAndDecrement());
|
||||
counter.remove(index);
|
||||
latch.countDown();
|
||||
requestsLatch.countDown();
|
||||
}
|
||||
}
|
||||
}, new Promise.Adapter<Stream>()
|
||||
|
@ -244,7 +251,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest
|
|||
}
|
||||
);
|
||||
}
|
||||
Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(requestsLatch.await(iterations, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(counter.toString(), counter.isEmpty());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.spdy.LEVEL=WARN
|
||||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
|
|
|
@ -572,7 +572,7 @@ public class Main
|
|||
|
||||
args.setAllModules(modules);
|
||||
List<Module> activeModules = modules.resolveEnabled();
|
||||
|
||||
|
||||
// 7) Lib & XML Expansion / Resolution
|
||||
args.expandModules(baseHome,activeModules);
|
||||
|
||||
|
|
|
@ -39,6 +39,13 @@ import java.util.regex.Pattern;
|
|||
public class Modules implements Iterable<Module>
|
||||
{
|
||||
private Map<String, Module> modules = new HashMap<>();
|
||||
/*
|
||||
* modules that may appear in the resolved graph but are undefined in the module system
|
||||
*
|
||||
* ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file)
|
||||
*/
|
||||
private Set<String> missingModules = new HashSet<String>();
|
||||
|
||||
private int maxDepth = -1;
|
||||
|
||||
private Set<String> asNameSet(Set<Module> moduleSet)
|
||||
|
@ -110,7 +117,7 @@ public class Modules implements Iterable<Module>
|
|||
|
||||
if (parent == null)
|
||||
{
|
||||
System.err.printf("WARNING: module not found [%s]%n",parentName);
|
||||
StartLog.debug("module not found [%s]%n",parentName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -124,7 +131,7 @@ public class Modules implements Iterable<Module>
|
|||
Module optional = get(optionalParentName);
|
||||
if (optional == null)
|
||||
{
|
||||
System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
|
||||
StartLog.debug("optional module not found [%s]%n",optionalParentName);
|
||||
}
|
||||
else if (optional.isEnabled())
|
||||
{
|
||||
|
@ -285,12 +292,12 @@ public class Modules implements Iterable<Module>
|
|||
}
|
||||
}
|
||||
|
||||
private void findParents(Module module, Set<Module> ret)
|
||||
private void findParents(Module module, Map<String, Module> ret)
|
||||
{
|
||||
ret.add(module);
|
||||
ret.put(module.getName(), module);
|
||||
for (Module parent : module.getParentEdges())
|
||||
{
|
||||
ret.add(parent);
|
||||
ret.put(parent.getName(), parent);
|
||||
findParents(parent,ret);
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +377,7 @@ public class Modules implements Iterable<Module>
|
|||
}
|
||||
|
||||
// load missing post-expanded dependent modules
|
||||
boolean done = false;
|
||||
boolean done = false;
|
||||
while (!done)
|
||||
{
|
||||
done = true;
|
||||
|
@ -380,7 +387,7 @@ public class Modules implements Iterable<Module>
|
|||
{
|
||||
for (String parent : m.getParentNames())
|
||||
{
|
||||
if (modules.containsKey(parent))
|
||||
if (modules.containsKey(parent) || missingModules.contains(parent))
|
||||
{
|
||||
continue; // found. skip it.
|
||||
}
|
||||
|
@ -392,8 +399,16 @@ public class Modules implements Iterable<Module>
|
|||
for (String missingParent : missingParents)
|
||||
{
|
||||
File file = basehome.getFile("modules/" + missingParent + ".mod");
|
||||
Module module = registerModule(basehome,args,file);
|
||||
updateParentReferencesTo(module);
|
||||
if ( FS.canReadFile(file) )
|
||||
{
|
||||
Module module = registerModule(basehome,args,file);
|
||||
updateParentReferencesTo(module);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartLog.debug("Missing module definition: [ Mod: %s | File: %s]", missingParent, file);
|
||||
missingModules.add(missingParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +440,7 @@ public class Modules implements Iterable<Module>
|
|||
*/
|
||||
public List<Module> resolveEnabled()
|
||||
{
|
||||
Set<Module> active = new HashSet<Module>();
|
||||
Map<String, Module> active = new HashMap<String,Module>();
|
||||
|
||||
for (Module module : modules.values())
|
||||
{
|
||||
|
@ -435,18 +450,37 @@ public class Modules implements Iterable<Module>
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* check against the missing modules
|
||||
*
|
||||
* Ex: npn should match anything under npn/
|
||||
*/
|
||||
for ( String missing : missingModules )
|
||||
{
|
||||
for (String activeModule: active.keySet())
|
||||
{
|
||||
if ( missing.startsWith(activeModule) )
|
||||
{
|
||||
StartLog.warn("** Unable to continue, required dependency missing. [%s]", missing);
|
||||
StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
|
||||
StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Module> ordered = new ArrayList<>();
|
||||
ordered.addAll(active);
|
||||
ordered.addAll(active.values());
|
||||
Collections.sort(ordered,new Module.DepthComparator());
|
||||
return ordered;
|
||||
}
|
||||
|
||||
public Set<String> resolveParentModulesOf(String moduleName)
|
||||
{
|
||||
Set<Module> ret = new HashSet<>();
|
||||
Map<String,Module> ret = new HashMap<>();
|
||||
Module module = get(moduleName);
|
||||
findParents(module,ret);
|
||||
return asNameSet(ret);
|
||||
return ret.keySet();
|
||||
}
|
||||
|
||||
private String toIndent(int depth)
|
||||
|
|
|
@ -98,8 +98,9 @@ public final class Props implements Iterable<Prop>
|
|||
|
||||
if (props.isEmpty())
|
||||
{
|
||||
// This is a stupid programming error, we should have something, even system properties
|
||||
throw new PropsException("Props is empty: no properties declared!?");
|
||||
// nothing to expand
|
||||
// this situation can occur from --add-to-startd on a new blank base directory
|
||||
return str;
|
||||
}
|
||||
|
||||
Pattern pat = Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})");
|
||||
|
|
|
@ -1,145 +1,154 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.start;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BaseHomeTest
|
||||
{
|
||||
private void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)
|
||||
{
|
||||
List<String> actual = new ArrayList<>();
|
||||
for (File file : files)
|
||||
{
|
||||
actual.add(hb.toShortForm(file));
|
||||
}
|
||||
Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFile_OnlyHome() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = null;
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
File startIni = hb.getFile("/start.ini");
|
||||
|
||||
String ref = hb.toShortForm(startIni);
|
||||
Assert.assertThat("Reference",ref,startsWith("${jetty.home}"));
|
||||
|
||||
String contents = IO.readToString(startIni);
|
||||
Assert.assertThat("Contents",contents,containsString("Home Ini"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFiles_OnlyHome() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = null;
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
List<File> files = hb.listFiles("/start.d");
|
||||
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("${jetty.home}/start.d/jmx.ini");
|
||||
expected.add("${jetty.home}/start.d/jndi.ini");
|
||||
expected.add("${jetty.home}/start.d/jsp.ini");
|
||||
expected.add("${jetty.home}/start.d/logging.ini");
|
||||
expected.add("${jetty.home}/start.d/ssl.ini");
|
||||
|
||||
assertFileList(hb,"Files found",expected,files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFiles_Filtered_OnlyHome() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = null;
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
List<File> files = hb.listFiles("/start.d",new FS.IniFilter());
|
||||
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("${jetty.home}/start.d/jmx.ini");
|
||||
expected.add("${jetty.home}/start.d/jndi.ini");
|
||||
expected.add("${jetty.home}/start.d/jsp.ini");
|
||||
expected.add("${jetty.home}/start.d/logging.ini");
|
||||
expected.add("${jetty.home}/start.d/ssl.ini");
|
||||
|
||||
assertFileList(hb,"Files found",expected,files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFiles_Both() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
List<File> files = hb.listFiles("/start.d");
|
||||
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("${jetty.base}/start.d/jmx.ini");
|
||||
expected.add("${jetty.home}/start.d/jndi.ini");
|
||||
expected.add("${jetty.home}/start.d/jsp.ini");
|
||||
expected.add("${jetty.base}/start.d/logging.ini");
|
||||
expected.add("${jetty.home}/start.d/ssl.ini");
|
||||
expected.add("${jetty.base}/start.d/myapp.ini");
|
||||
|
||||
assertFileList(hb,"Files found",expected,files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault() throws IOException
|
||||
{
|
||||
BaseHome bh = new BaseHome();
|
||||
Assert.assertThat("Home",bh.getHome(),notNullValue());
|
||||
Assert.assertThat("Base",bh.getBase(),notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFile_Both() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
File startIni = hb.getFile("/start.ini");
|
||||
|
||||
String ref = hb.toShortForm(startIni);
|
||||
Assert.assertThat("Reference",ref,startsWith("${jetty.base}"));
|
||||
|
||||
String contents = IO.readToString(startIni);
|
||||
Assert.assertThat("Contents",contents,containsString("Base Ini"));
|
||||
}
|
||||
}
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.start;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BaseHomeTest
|
||||
{
|
||||
private void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)
|
||||
{
|
||||
List<String> actual = new ArrayList<>();
|
||||
for (File file : files)
|
||||
{
|
||||
actual.add(hb.toShortForm(file));
|
||||
}
|
||||
Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));
|
||||
}
|
||||
|
||||
private void toOsSeparators(List<String> expected)
|
||||
{
|
||||
for (int i = 0; i < expected.size(); i++)
|
||||
{
|
||||
String fixed = FS.separators(expected.get(i));
|
||||
expected.set(i,fixed);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFile_OnlyHome() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = null;
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
File startIni = hb.getFile("/start.ini");
|
||||
|
||||
String ref = hb.toShortForm(startIni);
|
||||
Assert.assertThat("Reference",ref,startsWith("${jetty.home}"));
|
||||
|
||||
String contents = IO.readToString(startIni);
|
||||
Assert.assertThat("Contents",contents,containsString("Home Ini"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFiles_OnlyHome() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = null;
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
List<File> files = hb.listFiles("/start.d");
|
||||
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("${jetty.home}/start.d/jmx.ini");
|
||||
expected.add("${jetty.home}/start.d/jndi.ini");
|
||||
expected.add("${jetty.home}/start.d/jsp.ini");
|
||||
expected.add("${jetty.home}/start.d/logging.ini");
|
||||
expected.add("${jetty.home}/start.d/ssl.ini");
|
||||
toOsSeparators(expected);
|
||||
|
||||
assertFileList(hb,"Files found",expected,files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFiles_Filtered_OnlyHome() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = null;
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
List<File> files = hb.listFiles("/start.d",new FS.IniFilter());
|
||||
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("${jetty.home}/start.d/jmx.ini");
|
||||
expected.add("${jetty.home}/start.d/jndi.ini");
|
||||
expected.add("${jetty.home}/start.d/jsp.ini");
|
||||
expected.add("${jetty.home}/start.d/logging.ini");
|
||||
expected.add("${jetty.home}/start.d/ssl.ini");
|
||||
toOsSeparators(expected);
|
||||
|
||||
assertFileList(hb,"Files found",expected,files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFiles_Both() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
List<File> files = hb.listFiles("/start.d");
|
||||
|
||||
List<String> expected = new ArrayList<>();
|
||||
expected.add("${jetty.base}/start.d/jmx.ini");
|
||||
expected.add("${jetty.home}/start.d/jndi.ini");
|
||||
expected.add("${jetty.home}/start.d/jsp.ini");
|
||||
expected.add("${jetty.base}/start.d/logging.ini");
|
||||
expected.add("${jetty.home}/start.d/ssl.ini");
|
||||
expected.add("${jetty.base}/start.d/myapp.ini");
|
||||
toOsSeparators(expected);
|
||||
|
||||
assertFileList(hb,"Files found",expected,files);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault() throws IOException
|
||||
{
|
||||
BaseHome bh = new BaseHome();
|
||||
Assert.assertThat("Home",bh.getHome(),notNullValue());
|
||||
Assert.assertThat("Base",bh.getBase(),notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFile_Both() throws IOException
|
||||
{
|
||||
File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
|
||||
File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");
|
||||
|
||||
BaseHome hb = new BaseHome(homeDir,baseDir);
|
||||
File startIni = hb.getFile("/start.ini");
|
||||
|
||||
String ref = hb.toShortForm(startIni);
|
||||
Assert.assertThat("Reference",ref,startsWith("${jetty.base}"));
|
||||
|
||||
String contents = IO.readToString(startIni);
|
||||
Assert.assertThat("Contents",contents,containsString("Base Ini"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,274 +1,274 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.start;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.start.Props.Prop;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Assert;
|
||||
|
||||
public class ConfigurationAssert
|
||||
{
|
||||
/**
|
||||
* Given a provided StartArgs, assert that the configuration it has determined is valid based on values in a assert text file.
|
||||
*
|
||||
* @param baseHome
|
||||
* the BaseHome used. Access it via {@link Main#getBaseHome()}
|
||||
* @param args
|
||||
* the StartArgs that has been processed via {@link Main#processCommandLine(String[])}
|
||||
* @param filename
|
||||
* the filename of the assertion values
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException
|
||||
{
|
||||
File testResourcesDir = MavenTestingUtils.getTestResourcesDir();
|
||||
File file = MavenTestingUtils.getTestResourceFile(filename);
|
||||
TextFile textFile = new TextFile(file);
|
||||
|
||||
// Validate XMLs (order is important)
|
||||
List<String> expectedXmls = new ArrayList<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("XML|"))
|
||||
{
|
||||
expectedXmls.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualXmls = new ArrayList<>();
|
||||
for (File xml : args.getXmlFiles())
|
||||
{
|
||||
actualXmls.add(shorten(baseHome,xml,testResourcesDir));
|
||||
}
|
||||
assertOrdered("XML Resolution Order",expectedXmls,actualXmls);
|
||||
|
||||
// Validate LIBs (order is not important)
|
||||
List<String> expectedLibs = new ArrayList<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("LIB|"))
|
||||
{
|
||||
expectedLibs.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualLibs = new ArrayList<>();
|
||||
for (File path : args.getClasspath())
|
||||
{
|
||||
actualLibs.add(shorten(baseHome,path,testResourcesDir));
|
||||
}
|
||||
assertContainsUnordered("Libs",expectedLibs,actualLibs);
|
||||
|
||||
// Validate PROPERTIES (order is not important)
|
||||
Set<String> expectedProperties = new HashSet<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("PROP|"))
|
||||
{
|
||||
expectedProperties.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualProperties = new ArrayList<>();
|
||||
for (Prop prop : args.getProperties())
|
||||
{
|
||||
String name = prop.key;
|
||||
if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))
|
||||
{
|
||||
// strip these out from assertion, to make assertions easier.
|
||||
continue;
|
||||
}
|
||||
actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value));
|
||||
}
|
||||
assertContainsUnordered("Properties",expectedProperties,actualProperties);
|
||||
|
||||
// Validate Downloads
|
||||
List<String> expectedDownloads = new ArrayList<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("DOWNLOAD|"))
|
||||
{
|
||||
expectedDownloads.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualDownloads = new ArrayList<>();
|
||||
for (FileArg darg : args.getFiles())
|
||||
{
|
||||
if (darg.uri != null)
|
||||
{
|
||||
actualDownloads.add(String.format("%s:%s",darg.uri,darg.location));
|
||||
}
|
||||
}
|
||||
assertContainsUnordered("Downloads",expectedDownloads,actualDownloads);
|
||||
|
||||
// Validate Files/Dirs creation
|
||||
List<String> expectedFiles = new ArrayList<>();
|
||||
for(String line: textFile)
|
||||
{
|
||||
if(line.startsWith("FILE|"))
|
||||
{
|
||||
expectedFiles.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualFiles = new ArrayList<>();
|
||||
for(FileArg farg: args.getFiles())
|
||||
{
|
||||
if(farg.uri == null)
|
||||
{
|
||||
actualFiles.add(farg.location);
|
||||
}
|
||||
}
|
||||
assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles);
|
||||
}
|
||||
|
||||
private static String shorten(BaseHome baseHome, File path, File testResourcesDir)
|
||||
{
|
||||
String value = baseHome.toShortForm(path);
|
||||
if (value.startsWith(testResourcesDir.getAbsolutePath()))
|
||||
{
|
||||
int len = testResourcesDir.getAbsolutePath().length();
|
||||
value = "${maven-test-resources}" + value.substring(len);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void assertContainsUnordered(String msg, Collection<String> expectedSet, Collection<String> actualSet)
|
||||
{
|
||||
// same size?
|
||||
boolean mismatch = expectedSet.size() != actualSet.size();
|
||||
|
||||
// test content
|
||||
Set<String> missing = new HashSet<>();
|
||||
for (String expected : expectedSet)
|
||||
{
|
||||
if (!actualSet.contains(expected))
|
||||
{
|
||||
missing.add(expected);
|
||||
}
|
||||
}
|
||||
|
||||
if (mismatch || missing.size() > 0)
|
||||
{
|
||||
// build up detailed error message
|
||||
StringWriter message = new StringWriter();
|
||||
PrintWriter err = new PrintWriter(message);
|
||||
|
||||
err.printf("%s: Assert Contains (Unordered)",msg);
|
||||
if (mismatch)
|
||||
{
|
||||
err.print(" [size mismatch]");
|
||||
}
|
||||
if (missing.size() >= 0)
|
||||
{
|
||||
err.printf(" [%d entries missing]",missing.size());
|
||||
}
|
||||
err.println();
|
||||
err.printf("Actual Entries (size: %d)%n",actualSet.size());
|
||||
for (String actual : actualSet)
|
||||
{
|
||||
char indicator = expectedSet.contains(actual)?' ':'>';
|
||||
err.printf("%s| %s%n",indicator,actual);
|
||||
}
|
||||
err.printf("Expected Entries (size: %d)%n",expectedSet.size());
|
||||
for (String expected : expectedSet)
|
||||
{
|
||||
char indicator = actualSet.contains(expected)?' ':'>';
|
||||
err.printf("%s| %s%n",indicator,expected);
|
||||
}
|
||||
err.flush();
|
||||
Assert.fail(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)
|
||||
{
|
||||
// same size?
|
||||
boolean mismatch = expectedList.size() != actualList.size();
|
||||
|
||||
// test content
|
||||
List<Integer> badEntries = new ArrayList<>();
|
||||
int min = Math.min(expectedList.size(),actualList.size());
|
||||
int max = Math.max(expectedList.size(),actualList.size());
|
||||
for (int i = 0; i < min; i++)
|
||||
{
|
||||
if (!expectedList.get(i).equals(actualList.get(i)))
|
||||
{
|
||||
badEntries.add(i);
|
||||
}
|
||||
}
|
||||
for (int i = min; i < max; i++)
|
||||
{
|
||||
badEntries.add(i);
|
||||
}
|
||||
|
||||
if (mismatch || badEntries.size() > 0)
|
||||
{
|
||||
// build up detailed error message
|
||||
StringWriter message = new StringWriter();
|
||||
PrintWriter err = new PrintWriter(message);
|
||||
|
||||
err.printf("%s: Assert Contains (Unordered)",msg);
|
||||
if (mismatch)
|
||||
{
|
||||
err.print(" [size mismatch]");
|
||||
}
|
||||
if (badEntries.size() >= 0)
|
||||
{
|
||||
err.printf(" [%d entries not matched]",badEntries.size());
|
||||
}
|
||||
err.println();
|
||||
err.printf("Actual Entries (size: %d)%n",actualList.size());
|
||||
for (int i = 0; i < actualList.size(); i++)
|
||||
{
|
||||
String actual = actualList.get(i);
|
||||
char indicator = badEntries.contains(i)?'>':' ';
|
||||
err.printf("%s[%d] %s%n",indicator,i,actual);
|
||||
}
|
||||
|
||||
err.printf("Expected Entries (size: %d)%n",expectedList.size());
|
||||
for (int i = 0; i < expectedList.size(); i++)
|
||||
{
|
||||
String expected = expectedList.get(i);
|
||||
char indicator = badEntries.contains(i)?'>':' ';
|
||||
err.printf("%s[%d] %s%n",indicator,i,expected);
|
||||
}
|
||||
err.flush();
|
||||
Assert.fail(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static String getValue(String arg)
|
||||
{
|
||||
int idx = arg.indexOf('|');
|
||||
Assert.assertThat("Expecting '|' sign in [" + arg + "]",idx,greaterThanOrEqualTo(0));
|
||||
String value = arg.substring(idx + 1).trim();
|
||||
Assert.assertThat("Expecting Value after '|' in [" + arg + "]",value.length(),greaterThan(0));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.start;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.start.Props.Prop;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.Assert;
|
||||
|
||||
public class ConfigurationAssert
|
||||
{
|
||||
/**
|
||||
* Given a provided StartArgs, assert that the configuration it has determined is valid based on values in a assert text file.
|
||||
*
|
||||
* @param baseHome
|
||||
* the BaseHome used. Access it via {@link Main#getBaseHome()}
|
||||
* @param args
|
||||
* the StartArgs that has been processed via {@link Main#processCommandLine(String[])}
|
||||
* @param filename
|
||||
* the filename of the assertion values
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException
|
||||
{
|
||||
File testResourcesDir = MavenTestingUtils.getTestResourcesDir();
|
||||
File file = MavenTestingUtils.getTestResourceFile(filename);
|
||||
TextFile textFile = new TextFile(file);
|
||||
|
||||
// Validate XMLs (order is important)
|
||||
List<String> expectedXmls = new ArrayList<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("XML|"))
|
||||
{
|
||||
expectedXmls.add(FS.separators(getValue(line)));
|
||||
}
|
||||
}
|
||||
List<String> actualXmls = new ArrayList<>();
|
||||
for (File xml : args.getXmlFiles())
|
||||
{
|
||||
actualXmls.add(shorten(baseHome,xml,testResourcesDir));
|
||||
}
|
||||
assertOrdered("XML Resolution Order",expectedXmls,actualXmls);
|
||||
|
||||
// Validate LIBs (order is not important)
|
||||
List<String> expectedLibs = new ArrayList<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("LIB|"))
|
||||
{
|
||||
expectedLibs.add(FS.separators(getValue(line)));
|
||||
}
|
||||
}
|
||||
List<String> actualLibs = new ArrayList<>();
|
||||
for (File path : args.getClasspath())
|
||||
{
|
||||
actualLibs.add(shorten(baseHome,path,testResourcesDir));
|
||||
}
|
||||
assertContainsUnordered("Libs",expectedLibs,actualLibs);
|
||||
|
||||
// Validate PROPERTIES (order is not important)
|
||||
Set<String> expectedProperties = new HashSet<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("PROP|"))
|
||||
{
|
||||
expectedProperties.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualProperties = new ArrayList<>();
|
||||
for (Prop prop : args.getProperties())
|
||||
{
|
||||
String name = prop.key;
|
||||
if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))
|
||||
{
|
||||
// strip these out from assertion, to make assertions easier.
|
||||
continue;
|
||||
}
|
||||
actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value));
|
||||
}
|
||||
assertContainsUnordered("Properties",expectedProperties,actualProperties);
|
||||
|
||||
// Validate Downloads
|
||||
List<String> expectedDownloads = new ArrayList<>();
|
||||
for (String line : textFile)
|
||||
{
|
||||
if (line.startsWith("DOWNLOAD|"))
|
||||
{
|
||||
expectedDownloads.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualDownloads = new ArrayList<>();
|
||||
for (FileArg darg : args.getFiles())
|
||||
{
|
||||
if (darg.uri != null)
|
||||
{
|
||||
actualDownloads.add(String.format("%s:%s",darg.uri,darg.location));
|
||||
}
|
||||
}
|
||||
assertContainsUnordered("Downloads",expectedDownloads,actualDownloads);
|
||||
|
||||
// Validate Files/Dirs creation
|
||||
List<String> expectedFiles = new ArrayList<>();
|
||||
for(String line: textFile)
|
||||
{
|
||||
if(line.startsWith("FILE|"))
|
||||
{
|
||||
expectedFiles.add(getValue(line));
|
||||
}
|
||||
}
|
||||
List<String> actualFiles = new ArrayList<>();
|
||||
for(FileArg farg: args.getFiles())
|
||||
{
|
||||
if(farg.uri == null)
|
||||
{
|
||||
actualFiles.add(farg.location);
|
||||
}
|
||||
}
|
||||
assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles);
|
||||
}
|
||||
|
||||
private static String shorten(BaseHome baseHome, File path, File testResourcesDir)
|
||||
{
|
||||
String value = baseHome.toShortForm(path);
|
||||
if (value.startsWith(testResourcesDir.getAbsolutePath()))
|
||||
{
|
||||
int len = testResourcesDir.getAbsolutePath().length();
|
||||
value = "${maven-test-resources}" + value.substring(len);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void assertContainsUnordered(String msg, Collection<String> expectedSet, Collection<String> actualSet)
|
||||
{
|
||||
// same size?
|
||||
boolean mismatch = expectedSet.size() != actualSet.size();
|
||||
|
||||
// test content
|
||||
Set<String> missing = new HashSet<>();
|
||||
for (String expected : expectedSet)
|
||||
{
|
||||
if (!actualSet.contains(expected))
|
||||
{
|
||||
missing.add(expected);
|
||||
}
|
||||
}
|
||||
|
||||
if (mismatch || missing.size() > 0)
|
||||
{
|
||||
// build up detailed error message
|
||||
StringWriter message = new StringWriter();
|
||||
PrintWriter err = new PrintWriter(message);
|
||||
|
||||
err.printf("%s: Assert Contains (Unordered)",msg);
|
||||
if (mismatch)
|
||||
{
|
||||
err.print(" [size mismatch]");
|
||||
}
|
||||
if (missing.size() >= 0)
|
||||
{
|
||||
err.printf(" [%d entries missing]",missing.size());
|
||||
}
|
||||
err.println();
|
||||
err.printf("Actual Entries (size: %d)%n",actualSet.size());
|
||||
for (String actual : actualSet)
|
||||
{
|
||||
char indicator = expectedSet.contains(actual)?' ':'>';
|
||||
err.printf("%s| %s%n",indicator,actual);
|
||||
}
|
||||
err.printf("Expected Entries (size: %d)%n",expectedSet.size());
|
||||
for (String expected : expectedSet)
|
||||
{
|
||||
char indicator = actualSet.contains(expected)?' ':'>';
|
||||
err.printf("%s| %s%n",indicator,expected);
|
||||
}
|
||||
err.flush();
|
||||
Assert.fail(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)
|
||||
{
|
||||
// same size?
|
||||
boolean mismatch = expectedList.size() != actualList.size();
|
||||
|
||||
// test content
|
||||
List<Integer> badEntries = new ArrayList<>();
|
||||
int min = Math.min(expectedList.size(),actualList.size());
|
||||
int max = Math.max(expectedList.size(),actualList.size());
|
||||
for (int i = 0; i < min; i++)
|
||||
{
|
||||
if (!expectedList.get(i).equals(actualList.get(i)))
|
||||
{
|
||||
badEntries.add(i);
|
||||
}
|
||||
}
|
||||
for (int i = min; i < max; i++)
|
||||
{
|
||||
badEntries.add(i);
|
||||
}
|
||||
|
||||
if (mismatch || badEntries.size() > 0)
|
||||
{
|
||||
// build up detailed error message
|
||||
StringWriter message = new StringWriter();
|
||||
PrintWriter err = new PrintWriter(message);
|
||||
|
||||
err.printf("%s: Assert Contains (Unordered)",msg);
|
||||
if (mismatch)
|
||||
{
|
||||
err.print(" [size mismatch]");
|
||||
}
|
||||
if (badEntries.size() >= 0)
|
||||
{
|
||||
err.printf(" [%d entries not matched]",badEntries.size());
|
||||
}
|
||||
err.println();
|
||||
err.printf("Actual Entries (size: %d)%n",actualList.size());
|
||||
for (int i = 0; i < actualList.size(); i++)
|
||||
{
|
||||
String actual = actualList.get(i);
|
||||
char indicator = badEntries.contains(i)?'>':' ';
|
||||
err.printf("%s[%d] %s%n",indicator,i,actual);
|
||||
}
|
||||
|
||||
err.printf("Expected Entries (size: %d)%n",expectedList.size());
|
||||
for (int i = 0; i < expectedList.size(); i++)
|
||||
{
|
||||
String expected = expectedList.get(i);
|
||||
char indicator = badEntries.contains(i)?'>':' ';
|
||||
err.printf("%s[%d] %s%n",indicator,i,expected);
|
||||
}
|
||||
err.flush();
|
||||
Assert.fail(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static String getValue(String arg)
|
||||
{
|
||||
int idx = arg.indexOf('|');
|
||||
Assert.assertThat("Expecting '|' sign in [" + arg + "]",idx,greaterThanOrEqualTo(0));
|
||||
String value = arg.substring(idx + 1).trim();
|
||||
Assert.assertThat("Expecting Value after '|' in [" + arg + "]",value.length(),greaterThan(0));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,11 +61,24 @@ public class TestUseCases
|
|||
assertUseCase("home","base.jmx","assert-jmx.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithMissingNpnVersion() throws Exception
|
||||
{
|
||||
assertUseCase("home","base.missing.npn.version","assert-missing-npn-version.txt","java.version=1.7.0_01");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithSpdy() throws Exception
|
||||
{
|
||||
assertUseCase("home","base.enable.spdy","assert-enable-spdy.txt","java.version=1.7.0_21");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithSpdyBadNpnVersion() throws Exception
|
||||
{
|
||||
//StartLog.enableDebug();
|
||||
assertUseCase("home","base.enable.spdy.bad.npn.version","assert-enable-spdy-bad-npn-version.txt","java.version=1.7.0_01");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithDatabase() throws Exception
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
0
jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java
Executable file → Normal file
0
jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java
Executable file → Normal file
|
@ -20,7 +20,6 @@ package org.eclipse.jetty.util;
|
|||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
|
|
0
jetty-util/src/test/resources/TestData/test/subdir/subsubdir/alphabet
Executable file → Normal file
0
jetty-util/src/test/resources/TestData/test/subdir/subsubdir/alphabet
Executable file → Normal file
0
jetty-util/src/test/resources/TestData/test/subdir/subsubdir/numbers
Executable file → Normal file
0
jetty-util/src/test/resources/TestData/test/subdir/subsubdir/numbers
Executable file → Normal file
|
@ -397,6 +397,9 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
web_app = context.newResource(war);
|
||||
else
|
||||
web_app=context.getBaseResource();
|
||||
|
||||
if (web_app == null)
|
||||
throw new IllegalStateException("No resourceBase or war set for context");
|
||||
|
||||
// Accept aliases for WAR files
|
||||
if (web_app.getAlias() != null)
|
||||
|
@ -603,7 +606,7 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
if (resource == null)
|
||||
{
|
||||
if (context.getWar()==null || context.getWar().length()==0)
|
||||
resource=context.newResource(context.getResourceBase());
|
||||
throw new IllegalStateException("No resourceBase or war set for context");
|
||||
|
||||
// Set dir or WAR
|
||||
resource = context.newResource(context.getWar());
|
||||
|
@ -621,7 +624,8 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
|
||||
LOG.warn("Can't generate resourceBase as part of webapp tmp dir name: " + e);
|
||||
LOG.debug(e);
|
||||
}
|
||||
|
||||
//Context name
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.DeploymentException;
|
||||
|
@ -187,6 +186,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont
|
|||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
ShutdownThread.deregister(this);
|
||||
endpointClientMetadataCache.clear();
|
||||
super.doStop();
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.jsr356;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpointConfig.Configurator;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
|
@ -46,7 +46,23 @@ public class JsrUpgradeListener implements UpgradeListener
|
|||
|
||||
Map<String, List<String>> headers = request.getHeaders();
|
||||
configurator.beforeRequest(headers);
|
||||
request.setHeaders(headers);
|
||||
|
||||
// Handle cookies
|
||||
for (String name : headers.keySet())
|
||||
{
|
||||
if ("cookie".equalsIgnoreCase(name))
|
||||
{
|
||||
List<String> values = headers.get(name);
|
||||
if (values != null)
|
||||
{
|
||||
for (String cookie : values)
|
||||
{
|
||||
List<HttpCookie> cookies = HttpCookie.parse(cookie);
|
||||
request.getCookies().addAll(cookies);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -26,11 +26,13 @@ import java.util.Queue;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.DateDecoder;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.beans.TimeEncoder;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.ConfiguredEchoSocket;
|
||||
|
@ -38,6 +40,7 @@ import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoSocketConfigur
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +48,9 @@ import org.junit.Test;
|
|||
*/
|
||||
public class AnnotatedServerEndpointTest
|
||||
{
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
private static WSServer server;
|
||||
|
||||
@BeforeClass
|
||||
|
@ -72,7 +78,7 @@ public class AnnotatedServerEndpointTest
|
|||
|
||||
private void assertResponse(String message, String... expectedTexts) throws Exception
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
|
|
|
@ -23,10 +23,12 @@ import java.util.Queue;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpoint;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpointConfigContextListener;
|
||||
import org.junit.Assert;
|
||||
|
@ -42,6 +44,9 @@ public class BasicEndpointTest
|
|||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
@Test
|
||||
public void testEcho() throws Exception
|
||||
{
|
||||
|
@ -61,7 +66,7 @@ public class BasicEndpointTest
|
|||
wsb.deployWebapp(webapp);
|
||||
// wsb.dump();
|
||||
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.concurrent.Future;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -36,6 +37,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.IdleTimeoutContextListener;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutEndpoint;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutSocket;
|
||||
|
@ -51,6 +53,9 @@ public class IdleTimeoutTest
|
|||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
private static WSServer server;
|
||||
|
||||
@BeforeClass
|
||||
|
@ -80,7 +85,7 @@ public class IdleTimeoutTest
|
|||
|
||||
private void assertConnectionTimeout(URI uri) throws Exception, IOException, InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
|
|
|
@ -25,10 +25,12 @@ import java.util.Queue;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoConfiguredSocket;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
|
@ -42,6 +44,9 @@ public class LargeAnnotatedTest
|
|||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
@Test
|
||||
public void testEcho() throws Exception
|
||||
{
|
||||
|
@ -58,7 +63,7 @@ public class LargeAnnotatedTest
|
|||
wsb.deployWebapp(webapp);
|
||||
// wsb.dump();
|
||||
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.getPolicy().setMaxTextMessageSize(128*1024);
|
||||
|
|
|
@ -25,10 +25,12 @@ import java.util.Queue;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoDefaultSocket;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
|
@ -42,6 +44,9 @@ public class LargeContainerTest
|
|||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
@Test
|
||||
public void testEcho() throws Exception
|
||||
{
|
||||
|
@ -58,7 +63,7 @@ public class LargeContainerTest
|
|||
wsb.deployWebapp(webapp);
|
||||
// wsb.dump();
|
||||
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.getPolicy().setMaxTextMessageSize(128*1024);
|
||||
|
|
|
@ -23,10 +23,12 @@ import java.util.Queue;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoReturnEndpoint;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
|
@ -37,6 +39,9 @@ public class OnMessageReturnTest
|
|||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
@Test
|
||||
public void testEchoReturn() throws Exception
|
||||
{
|
||||
|
@ -53,7 +58,7 @@ public class OnMessageReturnTest
|
|||
wsb.deployWebapp(webapp);
|
||||
wsb.dump();
|
||||
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
|
|
|
@ -27,15 +27,19 @@ import java.util.List;
|
|||
import java.util.Queue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
@ -95,7 +99,11 @@ public class SessionTest
|
|||
return cases;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
private final Case testcase;
|
||||
private final static AtomicInteger ID = new AtomicInteger(0);
|
||||
private WSServer server;
|
||||
private URI serverUri;
|
||||
|
||||
|
@ -107,7 +115,7 @@ public class SessionTest
|
|||
@Before
|
||||
public void startServer() throws Exception
|
||||
{
|
||||
server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName()),"app");
|
||||
server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName() + "-" + ID.incrementAndGet()),"app");
|
||||
server.copyWebInf("empty-web.xml");
|
||||
server.copyClass(SessionInfoSocket.class);
|
||||
server.copyClass(SessionAltConfig.class);
|
||||
|
@ -127,7 +135,7 @@ public class SessionTest
|
|||
|
||||
private void assertResponse(String requestPath, String requestMessage, String expectedResponse) throws Exception
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
<module>websocket-client</module>
|
||||
<module>websocket-server</module>
|
||||
<module>websocket-servlet</module>
|
||||
<module>websocket-mux-extension</module>
|
||||
<module>javax-websocket-client-impl</module>
|
||||
<module>javax-websocket-server-impl</module>
|
||||
</modules>
|
||||
|
|
|
@ -262,7 +262,8 @@ public class UpgradeRequest
|
|||
|
||||
public void setCookies(List<HttpCookie> cookies)
|
||||
{
|
||||
this.cookies = cookies;
|
||||
this.cookies.clear();
|
||||
this.cookies.addAll(cookies);
|
||||
}
|
||||
|
||||
public void setExtensions(List<ExtensionConfig> configs)
|
||||
|
|
|
@ -24,10 +24,10 @@ import java.net.URI;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
|
@ -44,15 +44,17 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
|||
*/
|
||||
public class ClientUpgradeRequest extends UpgradeRequest
|
||||
{
|
||||
private final static Logger LOG = Log.getLogger(ClientUpgradeRequest.class);
|
||||
private final static int MAX_KEYS = -1; // maximum number of parameter keys to decode
|
||||
private static final Logger LOG = Log.getLogger(ClientUpgradeRequest.class);
|
||||
private static final int MAX_KEYS = -1; // maximum number of parameter keys to decode
|
||||
private static final Set<String> FORBIDDEN_HEADERS;
|
||||
|
||||
static
|
||||
{
|
||||
// headers not allowed to be set in ClientUpgradeRequest.headers
|
||||
FORBIDDEN_HEADERS = new HashSet<>();
|
||||
// Headers not allowed to be set in ClientUpgradeRequest.headers.
|
||||
FORBIDDEN_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
// Cookies are handled explicitly, avoid to add them twice.
|
||||
FORBIDDEN_HEADERS.add("cookie");
|
||||
// Headers that cannot be set by applications.
|
||||
FORBIDDEN_HEADERS.add("upgrade");
|
||||
FORBIDDEN_HEADERS.add("host");
|
||||
FORBIDDEN_HEADERS.add("connection");
|
||||
|
@ -176,7 +178,7 @@ public class ClientUpgradeRequest extends UpgradeRequest
|
|||
{
|
||||
if (FORBIDDEN_HEADERS.contains(key))
|
||||
{
|
||||
LOG.warn("Skipping forbidden header - {}",key);
|
||||
LOG.debug("Skipping forbidden header - {}",key);
|
||||
continue; // skip
|
||||
}
|
||||
request.append(key).append(": ");
|
||||
|
|
|
@ -94,6 +94,11 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
|
|||
{
|
||||
this(null,executor);
|
||||
}
|
||||
|
||||
public WebSocketClient(ByteBufferPool bufferPool)
|
||||
{
|
||||
this(null,null,bufferPool);
|
||||
}
|
||||
|
||||
public WebSocketClient(SslContextFactory sslContextFactory)
|
||||
{
|
||||
|
@ -101,11 +106,16 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
|
|||
}
|
||||
|
||||
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor)
|
||||
{
|
||||
this(sslContextFactory,executor,new MappedByteBufferPool());
|
||||
}
|
||||
|
||||
public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool)
|
||||
{
|
||||
this.executor = executor;
|
||||
this.sslContextFactory = sslContextFactory;
|
||||
this.policy = WebSocketPolicy.newClientPolicy();
|
||||
this.bufferPool = new MappedByteBufferPool();
|
||||
this.bufferPool = bufferPool;
|
||||
this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool);
|
||||
this.masker = new RandomMasker();
|
||||
this.eventDriverFactory = new EventDriverFactory(policy);
|
||||
|
@ -254,7 +264,7 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen
|
|||
}
|
||||
|
||||
super.doStop();
|
||||
LOG.info("Stopped {}",this);
|
||||
LOG.debug("Stopped {}",this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,10 +22,12 @@ import java.net.URI;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.common.test.BlockheadServer;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -41,13 +43,16 @@ public class BadNetworkTest
|
|||
@Rule
|
||||
public TestTracker tt = new TestTracker();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
private BlockheadServer server;
|
||||
private WebSocketClient client;
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client = new WebSocketClient(bufferPool);
|
||||
client.getPolicy().setIdleTimeout(250);
|
||||
client.start();
|
||||
}
|
||||
|
|
|
@ -33,12 +33,14 @@ import java.util.concurrent.Future;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeException;
|
||||
import org.eclipse.jetty.websocket.common.AcceptHash;
|
||||
import org.eclipse.jetty.websocket.common.test.BlockheadServer;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
|
@ -55,6 +57,9 @@ public class ClientConnectTest
|
|||
@Rule
|
||||
public TestTracker tt = new TestTracker();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool());
|
||||
|
||||
private final int timeout = 500;
|
||||
private BlockheadServer server;
|
||||
private WebSocketClient client;
|
||||
|
@ -86,7 +91,7 @@ public class ClientConnectTest
|
|||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
client = new WebSocketClient();
|
||||
client = new WebSocketClient(bufferPool);
|
||||
client.setConnectTimeout(timeout);
|
||||
client.start();
|
||||
}
|
||||
|
|
|
@ -29,11 +29,13 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool;
|
||||
|
||||
/**
|
||||
* This is not a general purpose websocket client. It's only for testing the websocket server and is hardwired to a specific draft version of the protocol.
|
||||
|
@ -95,6 +97,8 @@ public class TestClient
|
|||
|
||||
private static final Random __random = new Random();
|
||||
|
||||
private static LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("TestClient",new MappedByteBufferPool());
|
||||
|
||||
private final String _host;
|
||||
private final int _port;
|
||||
private final String _protocol;
|
||||
|
@ -172,7 +176,7 @@ public class TestClient
|
|||
}
|
||||
|
||||
TestClient[] client = new TestClient[clients];
|
||||
WebSocketClient wsclient = new WebSocketClient();
|
||||
WebSocketClient wsclient = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
wsclient.start();
|
||||
|
@ -250,6 +254,7 @@ public class TestClient
|
|||
|
||||
wsclient.stop();
|
||||
}
|
||||
bufferPool.assertNoLeaks();
|
||||
}
|
||||
|
||||
private static void usage(String[] args)
|
||||
|
|
|
@ -67,8 +67,7 @@ public class Parser
|
|||
private int cursor = 0;
|
||||
// Frame
|
||||
private WebSocketFrame frame;
|
||||
private Frame priorDataFrame;
|
||||
private byte lastDataOpcode;
|
||||
private boolean priorDataFrame;
|
||||
// payload specific
|
||||
private ByteBuffer payload;
|
||||
private int payloadLength;
|
||||
|
@ -186,7 +185,7 @@ public class Parser
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("{} Notify {}",policy.getBehavior(),incomingFramesHandler);
|
||||
LOG.debug("{} Notify {}",policy.getBehavior(),getIncomingFramesHandler());
|
||||
}
|
||||
|
||||
if (policy.getBehavior() == WebSocketBehavior.SERVER)
|
||||
|
@ -201,14 +200,15 @@ public class Parser
|
|||
* But we can't trust the client at this point, so Jetty opts to close
|
||||
* the connection as a Protocol error.
|
||||
*/
|
||||
if (f.isMasked() == false)
|
||||
if (!f.isMasked())
|
||||
{
|
||||
throw new ProtocolException("Client MUST mask all frames (RFC-6455: Section 5.1)");
|
||||
}
|
||||
} else if(policy.getBehavior() == WebSocketBehavior.CLIENT)
|
||||
}
|
||||
else if(policy.getBehavior() == WebSocketBehavior.CLIENT)
|
||||
{
|
||||
// Required by RFC-6455 / Section 5.1
|
||||
if (f.isMasked() == true)
|
||||
if (f.isMasked())
|
||||
{
|
||||
throw new ProtocolException("Server MUST NOT mask any frames (RFC-6455: Section 5.1)");
|
||||
}
|
||||
|
@ -258,30 +258,36 @@ public class Parser
|
|||
{
|
||||
LOG.debug("{} Parsed Frame: {}",policy.getBehavior(),frame);
|
||||
notifyFrame(frame);
|
||||
if (frame.isDataFrame() && frame.isFin())
|
||||
if (frame.isDataFrame())
|
||||
{
|
||||
priorDataFrame = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
priorDataFrame = frame;
|
||||
priorDataFrame = !frame.isFin();
|
||||
}
|
||||
reset();
|
||||
}
|
||||
}
|
||||
catch (WebSocketException e)
|
||||
{
|
||||
buffer.position(buffer.limit()); // consume remaining
|
||||
this.payload = null; // reset
|
||||
reset();
|
||||
notifyWebSocketException(e);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
buffer.position(buffer.limit()); // consume remaining
|
||||
this.payload = null; // reset
|
||||
reset();
|
||||
notifyWebSocketException(new WebSocketException(t));
|
||||
}
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
if (frame != null)
|
||||
frame.reset();
|
||||
frame = null;
|
||||
bufferPool.release(payload);
|
||||
payload = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the base framing protocol buffer.
|
||||
* <p>
|
||||
|
@ -295,11 +301,6 @@ public class Parser
|
|||
*/
|
||||
private boolean parseFrame(ByteBuffer buffer)
|
||||
{
|
||||
if (buffer.remaining() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG.debug("{} Parsing {} bytes",policy.getBehavior(),buffer.remaining());
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
|
@ -307,21 +308,15 @@ public class Parser
|
|||
{
|
||||
case START:
|
||||
{
|
||||
if ((frame != null) && (frame.isFin()))
|
||||
{
|
||||
frame.reset();
|
||||
}
|
||||
|
||||
// peek at byte
|
||||
byte b = buffer.get();
|
||||
boolean fin = ((b & 0x80) != 0);
|
||||
|
||||
byte opc = (byte)(b & 0x0F);
|
||||
byte opcode = opc;
|
||||
byte opcode = (byte)(b & 0x0F);
|
||||
|
||||
if (!OpCode.isKnown(opcode))
|
||||
{
|
||||
throw new ProtocolException("Unknown opcode: " + opc);
|
||||
throw new ProtocolException("Unknown opcode: " + opcode);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -335,36 +330,32 @@ public class Parser
|
|||
}
|
||||
|
||||
// base framing flags
|
||||
switch(opcode)
|
||||
switch(opcode)
|
||||
{
|
||||
case OpCode.TEXT:
|
||||
frame = new TextFrame();
|
||||
lastDataOpcode = opcode;
|
||||
// data validation
|
||||
if ((priorDataFrame != null) && (!priorDataFrame.isFin()))
|
||||
if (priorDataFrame)
|
||||
{
|
||||
throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
|
||||
}
|
||||
break;
|
||||
case OpCode.BINARY:
|
||||
frame = new BinaryFrame();
|
||||
lastDataOpcode = opcode;
|
||||
// data validation
|
||||
if ((priorDataFrame != null) && (!priorDataFrame.isFin()))
|
||||
if (priorDataFrame)
|
||||
{
|
||||
throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
|
||||
}
|
||||
break;
|
||||
case OpCode.CONTINUATION:
|
||||
frame = new ContinuationFrame();
|
||||
lastDataOpcode = opcode;
|
||||
// continuation validation
|
||||
if (priorDataFrame == null)
|
||||
if (!priorDataFrame)
|
||||
{
|
||||
throw new ProtocolException("CONTINUATION frame without prior !FIN");
|
||||
}
|
||||
// Be careful to use the original opcode
|
||||
opcode = lastDataOpcode;
|
||||
break;
|
||||
case OpCode.CLOSE:
|
||||
frame = new CloseFrame();
|
||||
|
@ -556,11 +547,13 @@ public class Parser
|
|||
|
||||
case PAYLOAD:
|
||||
{
|
||||
frame.assertValid();
|
||||
if (parsePayload(buffer))
|
||||
{
|
||||
// special check for close
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
// TODO: yuck. Don't create an object to do validation checks!
|
||||
new CloseInfo(frame);
|
||||
}
|
||||
state = State.START;
|
||||
|
@ -591,21 +584,18 @@ public class Parser
|
|||
|
||||
if (buffer.hasRemaining())
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
frame.assertValid();
|
||||
payload = bufferPool.acquire(payloadLength,false);
|
||||
BufferUtil.clearToFill(payload);
|
||||
}
|
||||
|
||||
// Create a small window of the incoming buffer to work with.
|
||||
// this should only show the payload itself, and not any more
|
||||
// bytes that could belong to the start of the next frame.
|
||||
ByteBuffer window = buffer.slice();
|
||||
int bytesExpected = payloadLength - payload.position();
|
||||
int bytesSoFar = payload == null ? 0 : payload.position();
|
||||
int bytesExpected = payloadLength - bytesSoFar;
|
||||
int bytesAvailable = buffer.remaining();
|
||||
int windowBytes = Math.min(bytesAvailable,bytesExpected);
|
||||
window.limit(window.position() + windowBytes);
|
||||
int windowBytes = Math.min(bytesAvailable, bytesExpected);
|
||||
int limit = buffer.limit();
|
||||
buffer.limit(buffer.position() + windowBytes);
|
||||
ByteBuffer window = buffer.slice();
|
||||
buffer.limit(limit);
|
||||
buffer.position(buffer.position() + window.remaining());
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
|
@ -613,17 +603,30 @@ public class Parser
|
|||
}
|
||||
|
||||
maskProcessor.process(window);
|
||||
int len = BufferUtil.put(window,payload);
|
||||
|
||||
buffer.position(buffer.position() + len); // update incoming buffer position
|
||||
|
||||
if (payload.position() >= payloadLength)
|
||||
if (window.remaining() == payloadLength)
|
||||
{
|
||||
BufferUtil.flipToFlush(payload,0);
|
||||
frame.setPayload(payload);
|
||||
this.payload = null;
|
||||
// We have the whole content, no need to copy.
|
||||
frame.setPayload(window);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
payload = bufferPool.acquire(payloadLength,false);
|
||||
BufferUtil.clearToFill(payload);
|
||||
}
|
||||
// Copy the payload.
|
||||
payload.put(window);
|
||||
|
||||
if (payload.position() == payloadLength)
|
||||
{
|
||||
BufferUtil.flipToFlush(payload, 0);
|
||||
frame.setPayload(payload);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -56,37 +56,42 @@ import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
|||
*/
|
||||
public abstract class WebSocketFrame implements Frame
|
||||
{
|
||||
public static WebSocketFrame copy(Frame copy)
|
||||
public static WebSocketFrame copy(Frame original)
|
||||
{
|
||||
WebSocketFrame frame = null;
|
||||
switch (copy.getOpCode())
|
||||
WebSocketFrame copy;
|
||||
switch (original.getOpCode())
|
||||
{
|
||||
case OpCode.BINARY:
|
||||
frame = new BinaryFrame();
|
||||
copy = new BinaryFrame();
|
||||
break;
|
||||
case OpCode.TEXT:
|
||||
frame = new TextFrame();
|
||||
copy = new TextFrame();
|
||||
break;
|
||||
case OpCode.CLOSE:
|
||||
frame = new CloseFrame();
|
||||
copy = new CloseFrame();
|
||||
break;
|
||||
case OpCode.CONTINUATION:
|
||||
frame = new ContinuationFrame();
|
||||
copy = new ContinuationFrame();
|
||||
break;
|
||||
case OpCode.PING:
|
||||
frame = new PingFrame();
|
||||
copy = new PingFrame();
|
||||
break;
|
||||
case OpCode.PONG:
|
||||
frame = new PongFrame();
|
||||
copy = new PongFrame();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot copy frame with opcode " + copy.getOpCode() + " - " + copy);
|
||||
throw new IllegalArgumentException("Cannot copy frame with opcode " + original.getOpCode() + " - " + original);
|
||||
}
|
||||
|
||||
frame.copyHeaders(copy);
|
||||
frame.setPayload(copy.getPayload());
|
||||
|
||||
return frame;
|
||||
copy.copyHeaders(original);
|
||||
ByteBuffer payload = original.getPayload();
|
||||
if (payload != null)
|
||||
{
|
||||
ByteBuffer payloadCopy = ByteBuffer.allocate(payload.remaining());
|
||||
payloadCopy.put(payload.slice()).flip();
|
||||
copy.setPayload(payloadCopy);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,12 +128,6 @@ public abstract class WebSocketFrame implements Frame
|
|||
|
||||
public abstract void assertValid();
|
||||
|
||||
protected void copy(WebSocketFrame copy, ByteBuffer payload)
|
||||
{
|
||||
copyHeaders(copy);
|
||||
setPayload(payload);
|
||||
}
|
||||
|
||||
protected void copyHeaders(Frame frame)
|
||||
{
|
||||
finRsvOp = 0x00;
|
||||
|
@ -348,13 +347,7 @@ public abstract class WebSocketFrame implements Frame
|
|||
*/
|
||||
public WebSocketFrame setPayload(ByteBuffer buf)
|
||||
{
|
||||
if (buf == null)
|
||||
{
|
||||
data = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
data = buf.slice();
|
||||
data = buf;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,15 +122,9 @@ public abstract class ControlFrame extends WebSocketFrame
|
|||
@Override
|
||||
public WebSocketFrame setPayload(ByteBuffer buf)
|
||||
{
|
||||
if (buf == null)
|
||||
if (buf != null && buf.remaining() > MAX_CONTROL_PAYLOAD)
|
||||
{
|
||||
data = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (buf.remaining() > ControlFrame.MAX_CONTROL_PAYLOAD)
|
||||
{
|
||||
throw new ProtocolException("Control Payloads can not exceed 125 bytes in length.");
|
||||
throw new ProtocolException("Control Payloads can not exceed " + MAX_CONTROL_PAYLOAD + " bytes in length.");
|
||||
}
|
||||
return super.setPayload(buf);
|
||||
}
|
||||
|
|
|
@ -78,13 +78,20 @@ public class DataFrame extends WebSocketFrame
|
|||
return true;
|
||||
}
|
||||
|
||||
public void releaseBuffer()
|
||||
public void reset()
|
||||
{
|
||||
// TODO: this is rather ugly.
|
||||
// The ByteBufferPool is set only from extensions that
|
||||
// compress the payload. It would be better to wrap the
|
||||
// callback associated with this DataFrame into one that
|
||||
// releases the buffer and then call the nested callback,
|
||||
// rather than null-checking whether the pool exists and
|
||||
// if so then release the buffer.
|
||||
if (pool!=null)
|
||||
{
|
||||
pool.release(this.data);
|
||||
this.data=null;
|
||||
}
|
||||
super.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -358,16 +358,9 @@ public class FrameFlusher
|
|||
headerBuffer = null;
|
||||
}
|
||||
|
||||
if (!frame.hasPayload())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame instanceof DataFrame)
|
||||
{
|
||||
// TODO null payload within frame
|
||||
DataFrame data = (DataFrame)frame;
|
||||
data.releaseBuffer();
|
||||
((DataFrame)frame).reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,25 +41,19 @@ public class MessageInputStream extends InputStream implements MessageAppender
|
|||
private static final Logger LOG = Log.getLogger(MessageInputStream.class);
|
||||
// EOF (End of Buffers)
|
||||
private final static ByteBuffer EOF = ByteBuffer.allocate(0).asReadOnlyBuffer();
|
||||
/**
|
||||
* Used for controlling read suspend/resume behavior if the queue is full, but the read operations haven't caught up yet.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private final LogicalConnection connection;
|
||||
|
||||
private final BlockingDeque<ByteBuffer> buffers = new LinkedBlockingDeque<>();
|
||||
private AtomicBoolean closed = new AtomicBoolean(false);
|
||||
private final long timeoutMs;
|
||||
private ByteBuffer activeBuffer = null;
|
||||
private long timeoutMs = -1;
|
||||
|
||||
public MessageInputStream(LogicalConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
this.timeoutMs = -1; // disabled
|
||||
this(connection, -1);
|
||||
}
|
||||
|
||||
public MessageInputStream(LogicalConnection connection, int timeoutMs)
|
||||
{
|
||||
this.connection = connection;
|
||||
this.timeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
|
@ -71,16 +65,22 @@ public class MessageInputStream extends InputStream implements MessageAppender
|
|||
LOG.debug("appendMessage(ByteBuffer,{}): {}",fin,BufferUtil.toDetailString(framePayload));
|
||||
}
|
||||
|
||||
// if closed, we should just toss incoming payloads into the bit bucket.
|
||||
// If closed, we should just toss incoming payloads into the bit bucket.
|
||||
if (closed.get())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Put the payload into the queue
|
||||
// Put the payload into the queue, by copying it.
|
||||
// Copying is necessary because the payload will
|
||||
// be processed after this method returns.
|
||||
try
|
||||
{
|
||||
buffers.put(framePayload);
|
||||
int capacity = framePayload.remaining();
|
||||
// TODO: the copy buffer should be pooled too, but no buffer pool available from here.
|
||||
ByteBuffer copy = framePayload.isDirect() ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity);
|
||||
copy.put(framePayload).flip();
|
||||
buffers.put(copy);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
|
@ -141,7 +141,7 @@ public class MessageInputStream extends InputStream implements MessageAppender
|
|||
// grab a fresh buffer
|
||||
while (activeBuffer == null || !activeBuffer.hasRemaining())
|
||||
{
|
||||
if (timeoutMs == -1)
|
||||
if (timeoutMs < 0)
|
||||
{
|
||||
// infinite take
|
||||
activeBuffer = buffers.take();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue