Merge branch 'master' into javawebsocket-jsr
This commit is contained in:
commit
540a41bdc5
|
@ -191,25 +191,17 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
params.append("&");
|
||||
}
|
||||
|
||||
// Behave as a GET, adding the params to the path, if it's a POST with some content
|
||||
if (method == HttpMethod.POST && request.getContent() != null)
|
||||
method = HttpMethod.GET;
|
||||
|
||||
switch (method)
|
||||
// POST with no content, send parameters as body
|
||||
if (method == HttpMethod.POST && request.getContent() == null)
|
||||
{
|
||||
case GET:
|
||||
{
|
||||
path += "?";
|
||||
path += params.toString();
|
||||
request.path(path);
|
||||
break;
|
||||
}
|
||||
case POST:
|
||||
{
|
||||
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString());
|
||||
request.content(new StringContentProvider(params.toString()));
|
||||
break;
|
||||
}
|
||||
request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.FORM_ENCODED.asString());
|
||||
request.content(new StringContentProvider(params.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
path += "?";
|
||||
path += params.toString();
|
||||
request.path(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -254,6 +254,38 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PUT_WithParameters() throws Exception
|
||||
{
|
||||
final String paramName = "a";
|
||||
final String paramValue = "\u20AC";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
String value = request.getParameter(paramName);
|
||||
if (paramValue.equals(value))
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("text/plain");
|
||||
response.getOutputStream().print(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort() + "/path?" + paramName + "=" + paramValue);
|
||||
ContentResponse response = client.newRequest(uri)
|
||||
.method(HttpMethod.PUT)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_POST_WithParameters_WithContent() throws Exception
|
||||
{
|
||||
|
|
|
@ -142,14 +142,11 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
|
|||
@Override
|
||||
protected void onIdleExpired(TimeoutException timeout)
|
||||
{
|
||||
if (isOutputShutdown() || _fillInterest.isInterested() || _writeFlusher.isInProgress())
|
||||
{
|
||||
boolean output_shutdown=isOutputShutdown();
|
||||
_fillInterest.onFail(timeout);
|
||||
_writeFlusher.onFail(timeout);
|
||||
if (output_shutdown)
|
||||
close();
|
||||
}
|
||||
boolean output_shutdown=isOutputShutdown();
|
||||
_fillInterest.onFail(timeout);
|
||||
_writeFlusher.onFail(timeout);
|
||||
if (output_shutdown)
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -167,6 +167,20 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
selector.submit(selector.new Accept(channel));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Registers a channel to perform non-blocking read/write operations.</p>
|
||||
* <p>This method is called just after a channel has been accepted by {@link ServerSocketChannel#accept()},
|
||||
* or just after having performed a blocking connect via {@link Socket#connect(SocketAddress, int)}.</p>
|
||||
*
|
||||
* @param channel the channel to register
|
||||
* @param attachment An attachment to be passed via the selection key to the {@link SelectorManager#newConnection(SocketChannel, EndPoint, Object)} method.
|
||||
*/
|
||||
public void accept(final SocketChannel channel, Object attachment)
|
||||
{
|
||||
final ManagedSelector selector = chooseSelector();
|
||||
selector.submit(selector.new Accept(channel, attachment));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
|
@ -685,10 +699,18 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
private class Accept implements Runnable
|
||||
{
|
||||
private final SocketChannel _channel;
|
||||
private final Object _attachment;
|
||||
|
||||
public Accept(SocketChannel channel)
|
||||
{
|
||||
this._channel = channel;
|
||||
this._attachment = null;
|
||||
}
|
||||
|
||||
public Accept(SocketChannel channel, Object attachment)
|
||||
{
|
||||
this._channel = channel;
|
||||
this._attachment = attachment;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -696,7 +718,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
|
|||
{
|
||||
try
|
||||
{
|
||||
SelectionKey key = _channel.register(_selector, 0, null);
|
||||
SelectionKey key = _channel.register(_selector, 0, _attachment);
|
||||
EndPoint endpoint = createEndPoint(_channel, key);
|
||||
key.attach(endpoint);
|
||||
}
|
||||
|
|
|
@ -759,7 +759,6 @@ public class SslConnection extends AbstractConnection
|
|||
if (DEBUG)
|
||||
LOG.debug("{} renegotiation denied", SslConnection.this);
|
||||
shutdownOutput();
|
||||
BufferUtil.clear(_encryptedOutput);
|
||||
return allConsumed;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>9.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.eclipse.jetty.npn</groupId>
|
||||
<artifactId>npn-api</artifactId>
|
||||
<version>1.1.1-SNAPSHOT</version>
|
||||
<name>Jetty :: Next Protocol Negotiation :: API</name>
|
||||
|
||||
<properties>
|
||||
<!-- for now we do make it an OSGi bundle...
|
||||
but it needs to be in the bootstrap classes at runtime. -->
|
||||
<bundle-symbolic-name>org.eclipse.jetty.npn</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</connection>
|
||||
<developerConnection>scm:git:ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</developerConnection>
|
||||
<url>http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-npn</url>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.eclipse.jetty.npn.*;version="9.0"</Export-Package>
|
||||
<Import-Package>*</Import-Package>
|
||||
<Bundle-Description>Next Protocol Negotiation API. must be in the bootstrap packages at runtime.</Bundle-Description>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--
|
||||
Required for OSGI
|
||||
-->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<configuration>
|
||||
<useReleaseProfile>false</useReleaseProfile>
|
||||
<goals>deploy</goals>
|
||||
<arguments>-Peclipse-release</arguments>
|
||||
<preparationGoals>clean install</preparationGoals>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1,248 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
|
||||
package org.eclipse.jetty.npn;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
/**
|
||||
* <p>{@link NextProtoNego} provides an API to applications that want to make use of the
|
||||
* <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next Protocol Negotiation</a>.</p>
|
||||
* <p>The NPN extension is only available when using the TLS protocol, therefore applications must
|
||||
* ensure that the TLS protocol is used:</p>
|
||||
* <pre>
|
||||
* SSLContext context = SSLContext.getInstance("TLSv1");
|
||||
* </pre>
|
||||
* <p>Refer to the
|
||||
* <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext">list
|
||||
* of standard SSLContext protocol names</a> for further information on TLS protocol versions supported.</p>
|
||||
* <p>Applications must register instances of either {@link SSLSocket} or {@link SSLEngine} with a
|
||||
* {@link ClientProvider} or with a {@link ServerProvider}, depending whether they are on client or
|
||||
* server side.</p>
|
||||
* <p>The NPN implementation will invoke the provider callbacks to allow applications to interact
|
||||
* with the negotiation of the next protocol.</p>
|
||||
* <p>Client side typical usage:</p>
|
||||
* <pre>
|
||||
* SSLSocket sslSocket = ...;
|
||||
* NextProtoNego.put(sslSocket, new NextProtoNego.ClientProvider()
|
||||
* {
|
||||
* @Override
|
||||
* public boolean supports()
|
||||
* {
|
||||
* return true;
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void unsupported()
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public String selectProtocol(List<String> protocols)
|
||||
* {
|
||||
* return protocols.get(0);
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
* <p>Server side typical usage:</p>
|
||||
* <pre>
|
||||
* SSLSocket sslSocket = ...;
|
||||
* NextProtoNego.put(sslSocket, new NextProtoNego.ServerProvider()
|
||||
* {
|
||||
* @Override
|
||||
* public void unsupported()
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public List<String> protocols()
|
||||
* {
|
||||
* return Arrays.asList("http/1.1");
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void protocolSelected(String protocol)
|
||||
* {
|
||||
* System.out.println("Protocol Selected is: " + protocol);
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
* <p>There is no need to unregister {@link SSLSocket} or {@link SSLEngine} instances, as they
|
||||
* are kept in a {@link WeakHashMap} and will be garbage collected when the application does not
|
||||
* hard reference them anymore. However, methods to explicitly unregister {@link SSLSocket} or
|
||||
* {@link SSLEngine} instances are provided.</p>
|
||||
* <p>In order to help application development, you can set the {@link NextProtoNego#debug} field
|
||||
* to {@code true} to have debug code printed to {@link System#err}.</p>
|
||||
*/
|
||||
public class NextProtoNego
|
||||
{
|
||||
/**
|
||||
* <p>Enables debug logging on {@link System#err}.</p>
|
||||
*/
|
||||
public static boolean debug = false;
|
||||
|
||||
private static Map<Object, Provider> objects = Collections.synchronizedMap(new WeakHashMap<Object, Provider>());
|
||||
|
||||
private NextProtoNego()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Registers a SSLSocket with a provider.</p>
|
||||
*
|
||||
* @param socket the socket to register with the provider
|
||||
* @param provider the provider to register with the socket
|
||||
* @see #remove(SSLSocket)
|
||||
*/
|
||||
public static void put(SSLSocket socket, Provider provider)
|
||||
{
|
||||
objects.put(socket, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param socket a socket registered with {@link #put(SSLSocket, Provider)}
|
||||
* @return the provider registered with the given socket
|
||||
*/
|
||||
public static Provider get(SSLSocket socket)
|
||||
{
|
||||
return objects.get(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unregisters the given SSLSocket.</p>
|
||||
*
|
||||
* @param socket the socket to unregister
|
||||
* @return the provider registered with the socket
|
||||
* @see #put(SSLSocket, Provider)
|
||||
*/
|
||||
public static Provider remove(SSLSocket socket)
|
||||
{
|
||||
return objects.remove(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Registers a SSLEngine with a provider.</p>
|
||||
*
|
||||
* @param engine the engine to register with the provider
|
||||
* @param provider the provider to register with the engine
|
||||
* @see #remove(SSLEngine)
|
||||
*/
|
||||
public static void put(SSLEngine engine, Provider provider)
|
||||
{
|
||||
objects.put(engine, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param engine an engine registered with {@link #put(SSLEngine, Provider)}
|
||||
* @return the provider registered with the given engine
|
||||
*/
|
||||
public static Provider get(SSLEngine engine)
|
||||
{
|
||||
return objects.get(engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Unregisters the given SSLEngine.</p>
|
||||
*
|
||||
* @param engine the engine to unregister
|
||||
* @return the provider registered with the engine
|
||||
* @see #put(SSLEngine, Provider)
|
||||
*/
|
||||
public static Provider remove(SSLEngine engine)
|
||||
{
|
||||
return objects.remove(engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Base, empty, interface for providers.</p>
|
||||
*/
|
||||
public interface Provider
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The client-side provider interface that applications must implement to interact
|
||||
* with the negotiation of the next protocol.</p>
|
||||
*/
|
||||
public interface ClientProvider extends Provider
|
||||
{
|
||||
/**
|
||||
* <p>Callback invoked to let the implementation know whether an
|
||||
* empty NPN extension should be added to a ClientHello SSL message.</p>
|
||||
*
|
||||
* @return true to add the NPN extension, false otherwise
|
||||
*/
|
||||
public boolean supports();
|
||||
|
||||
/**
|
||||
* <p>Callback invoked to let the application know that the server does
|
||||
* not support NPN.</p>
|
||||
*/
|
||||
public void unsupported();
|
||||
|
||||
/**
|
||||
* <p>Callback invoked to let the application select a protocol
|
||||
* among the ones sent by the server.</p>
|
||||
*
|
||||
* @param protocols the protocols sent by the server
|
||||
* @return the protocol selected by the application, or null if the
|
||||
* NextProtocol SSL message should not be sent to the server
|
||||
*/
|
||||
public String selectProtocol(List<String> protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The server-side provider interface that applications must implement to interact
|
||||
* with the negotiation of the next protocol.</p>
|
||||
*/
|
||||
public interface ServerProvider extends Provider
|
||||
{
|
||||
/**
|
||||
* <p>Callback invoked to let the application know that the client does not
|
||||
* support NPN.</p>
|
||||
*/
|
||||
public void unsupported();
|
||||
|
||||
/**
|
||||
* <p>Callback invoked to let the implementation know the list
|
||||
* of protocols that should be added to an NPN extension in a
|
||||
* ServerHello SSL message.</p>
|
||||
* <p>This callback is invoked only if the client sent a NPN extension.</p>
|
||||
*
|
||||
* @return the list of protocols, or null if no NPN extension
|
||||
* should be sent to the client
|
||||
*/
|
||||
public List<String> protocols();
|
||||
|
||||
/**
|
||||
* <p>Callback invoked to let the application know the protocol selected
|
||||
* by the client.</p>
|
||||
* <p>This callback is invoked only if the client sent a NextProtocol SSL message.</p>
|
||||
*
|
||||
* @param protocol the selected protocol
|
||||
*/
|
||||
public void protocolSelected(String protocol);
|
||||
}
|
||||
}
|
|
@ -696,6 +696,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
|||
|
||||
HttpConfiguration httpConfig = HttpChannel.getCurrentHttpChannel().getHttpConfiguration();
|
||||
|
||||
|
||||
if (dataConstraint == UserDataConstraint.Confidential || dataConstraint == UserDataConstraint.Integral)
|
||||
{
|
||||
if (request.isSecure())
|
||||
|
@ -703,11 +704,13 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
|||
|
||||
if (httpConfig.getSecurePort() > 0)
|
||||
{
|
||||
String url = httpConfig.getSecureScheme() + "://" + request.getServerName() + ":" + httpConfig.getSecurePort()
|
||||
+ request.getRequestURI();
|
||||
String scheme = httpConfig.getSecureScheme();
|
||||
int port = httpConfig.getSecurePort();
|
||||
String url = ("https".equalsIgnoreCase(scheme) && port==443)
|
||||
? "https://"+request.getServerName()+request.getRequestURI()
|
||||
: scheme + "://" + request.getServerName() + ":" + port + request.getRequestURI();
|
||||
if (request.getQueryString() != null)
|
||||
url += "?" + request.getQueryString();
|
||||
|
||||
response.setContentLength(0);
|
||||
response.sendRedirect(url);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
|
||||
import org.eclipse.jetty.security.authentication.FormAuthenticator;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -65,12 +67,14 @@ public class ConstraintTest
|
|||
private Server _server;
|
||||
private LocalConnector _connector;
|
||||
private ConstraintSecurityHandler _security;
|
||||
private HttpConfiguration _config;
|
||||
|
||||
@Before
|
||||
public void startServer()
|
||||
{
|
||||
_server = new Server();
|
||||
_connector = new LocalConnector(_server);
|
||||
_config=_connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
|
||||
_server.setConnectors(new Connector[]{_connector});
|
||||
|
||||
ContextHandler _context = new ContextHandler();
|
||||
|
@ -161,7 +165,15 @@ public class ConstraintTest
|
|||
mapping5.setConstraint(constraint5);
|
||||
mapping5.setMethod("POST");
|
||||
|
||||
return Arrays.asList(mapping0, mapping1, mapping2, mapping3, mapping4, mapping5);
|
||||
Constraint constraint6 = new Constraint();
|
||||
constraint6.setAuthenticate(false);
|
||||
constraint6.setName("data constraint");
|
||||
constraint6.setDataConstraint(2);
|
||||
ConstraintMapping mapping6 = new ConstraintMapping();
|
||||
mapping6.setPathSpec("/data/*");
|
||||
mapping6.setConstraint(constraint6);
|
||||
|
||||
return Arrays.asList(mapping0, mapping1, mapping2, mapping3, mapping4, mapping5, mapping6);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -742,9 +754,9 @@ public class ConstraintTest
|
|||
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
|
||||
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
|
||||
|
||||
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
|
||||
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\nHost:wibble.com:8888\r\n\r\n");
|
||||
assertThat(response,containsString(" 302 Found"));
|
||||
assertThat(response,containsString("/ctx/testLoginPage"));
|
||||
assertThat(response,containsString("http://wibble.com:8888/ctx/testLoginPage"));
|
||||
|
||||
String session = response.substring(response.indexOf("JSESSIONID=") + 11, response.indexOf(";Path=/ctx"));
|
||||
|
||||
|
@ -840,6 +852,48 @@ public class ConstraintTest
|
|||
assertThat(response,startsWith("HTTP/1.1 200 OK"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testDataRedirection() throws Exception
|
||||
{
|
||||
_security.setAuthenticator(new BasicAuthenticator());
|
||||
_server.start();
|
||||
|
||||
String response;
|
||||
|
||||
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\n\r\n");
|
||||
assertTrue(response.startsWith("HTTP/1.1 403"));
|
||||
|
||||
_config.setSecurePort(8443);
|
||||
_config.setSecureScheme("https");
|
||||
|
||||
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\n\r\n");
|
||||
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
|
||||
assertTrue(response.indexOf("Location") > 0);
|
||||
assertTrue(response.indexOf(":8443/ctx/data/info") > 0);
|
||||
|
||||
_config.setSecurePort(443);
|
||||
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\n\r\n");
|
||||
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
|
||||
assertTrue(response.indexOf("Location") > 0);
|
||||
assertTrue(response.indexOf(":443/ctx/data/info") < 0);
|
||||
|
||||
_config.setSecurePort(8443);
|
||||
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\nHost: wobble.com\r\n\r\n");
|
||||
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
|
||||
assertTrue(response.indexOf("Location") > 0);
|
||||
assertTrue(response.indexOf("https://wobble.com:8443/ctx/data/info") > 0);
|
||||
|
||||
_config.setSecurePort(443);
|
||||
response = _connector.getResponses("GET /ctx/data/info HTTP/1.0\r\nHost: wobble.com\r\n\r\n");
|
||||
System.err.println(response);
|
||||
assertTrue(response.startsWith("HTTP/1.1 302 Found"));
|
||||
assertTrue(response.indexOf("Location") > 0);
|
||||
assertTrue(response.indexOf(":443") < 0);
|
||||
assertTrue(response.indexOf("https://wobble.com/ctx/data/info") > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoleRef() throws Exception
|
||||
{
|
||||
|
|
|
@ -151,6 +151,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
|
|||
private long _idleTimeout = 30000;
|
||||
private String _defaultProtocol;
|
||||
private ConnectionFactory _defaultConnectionFactory;
|
||||
private String _name;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -524,11 +525,29 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
|
|||
return _scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set a connector name. A context may be configured with
|
||||
* virtual hosts in the form "@contextname" and will only serve
|
||||
* requests from the named connector,
|
||||
* @param name A connector name.
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
_name=name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x{%s}",
|
||||
getClass().getSimpleName(),
|
||||
_name==null?getClass().getSimpleName():_name,
|
||||
hashCode(),
|
||||
getDefaultProtocol());
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.Executor;
|
|||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.Graceful;
|
||||
|
@ -90,4 +91,15 @@ public interface Connector extends LifeCycle, Graceful
|
|||
* @return immutable collection of connected endpoints
|
||||
*/
|
||||
public Collection<EndPoint> getConnectedEndPoints();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Get the connector name if set.
|
||||
* <p>A {@link ContextHandler} may be configured with
|
||||
* virtual hosts in the form "@connectorName" and will only serve
|
||||
* requests from the named connector.
|
||||
* @return The connector name or null.
|
||||
*/
|
||||
public String getName();
|
||||
}
|
||||
|
|
|
@ -157,7 +157,8 @@ public class ShutdownMonitor
|
|||
{
|
||||
return;
|
||||
}
|
||||
System.err.println("Starting ShutdownMonitorThread");
|
||||
if (DEBUG)
|
||||
System.err.println("Starting ShutdownMonitorThread");
|
||||
super.start();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.ClassLoaderDump;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Dispatcher;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HandlerContainer;
|
||||
|
@ -280,7 +281,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
*
|
||||
* @param vhosts
|
||||
* Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
|
||||
* String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
|
||||
* String representation of IP addresses. Host names may start with '*.' to wildcard one level of names. Hosts may start with '@', in which case they
|
||||
* will match the {@link Connector#getName()} for the request.
|
||||
*/
|
||||
public void setVirtualHosts(String[] vhosts)
|
||||
{
|
||||
|
@ -872,18 +874,29 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
|
||||
boolean match = false;
|
||||
|
||||
for (int i = 0; !match && i < _vhosts.length; i++)
|
||||
loop: for (String contextVhost:_vhosts)
|
||||
{
|
||||
String contextVhost = _vhosts[i];
|
||||
if (contextVhost == null)
|
||||
if (contextVhost == null || contextVhost.length()==0)
|
||||
continue;
|
||||
if (contextVhost.startsWith("*."))
|
||||
char c=contextVhost.charAt(0);
|
||||
switch (c)
|
||||
{
|
||||
// wildcard only at the beginning, and only for one additional subdomain level
|
||||
match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
|
||||
case '*':
|
||||
if (contextVhost.startsWith("*."))
|
||||
// wildcard only at the beginning, and only for one additional subdomain level
|
||||
match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
|
||||
break;
|
||||
case '@':
|
||||
String name=baseRequest.getHttpChannel().getConnector().getName();
|
||||
match=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
|
||||
break;
|
||||
default:
|
||||
match = contextVhost.equalsIgnoreCase(vhost);
|
||||
}
|
||||
else
|
||||
match = contextVhost.equalsIgnoreCase(vhost);
|
||||
|
||||
if (match)
|
||||
break loop;
|
||||
|
||||
}
|
||||
if (!match)
|
||||
return false;
|
||||
|
|
|
@ -118,6 +118,79 @@ public class ContextHandlerTest
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNamedConnector() throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
LocalConnector connector = new LocalConnector(server);
|
||||
LocalConnector connectorN = new LocalConnector(server);
|
||||
connectorN.setName("name");
|
||||
server.setConnectors(new Connector[] { connector, connectorN });
|
||||
|
||||
ContextHandler contextA = new ContextHandler("/");
|
||||
contextA.setVirtualHosts(new String[]{"www.example.com" });
|
||||
IsHandledHandler handlerA = new IsHandledHandler();
|
||||
contextA.setHandler(handlerA);
|
||||
|
||||
ContextHandler contextB = new ContextHandler("/");
|
||||
IsHandledHandler handlerB = new IsHandledHandler();
|
||||
contextB.setHandler(handlerB);
|
||||
contextB.setVirtualHosts(new String[]{ "@name" });
|
||||
|
||||
ContextHandler contextC = new ContextHandler("/");
|
||||
IsHandledHandler handlerC = new IsHandledHandler();
|
||||
contextC.setHandler(handlerC);
|
||||
|
||||
HandlerCollection c = new HandlerCollection();
|
||||
c.addHandler(contextA);
|
||||
c.addHandler(contextB);
|
||||
c.addHandler(contextC);
|
||||
server.setHandler(c);
|
||||
|
||||
server.start();
|
||||
try
|
||||
{
|
||||
connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example.com.\n\n");
|
||||
assertTrue(handlerA.isHandled());
|
||||
assertFalse(handlerB.isHandled());
|
||||
assertFalse(handlerC.isHandled());
|
||||
handlerA.reset();
|
||||
handlerB.reset();
|
||||
handlerC.reset();
|
||||
|
||||
connector.getResponses("GET / HTTP/1.0\n" + "Host: localhost\n\n");
|
||||
assertFalse(handlerA.isHandled());
|
||||
assertFalse(handlerB.isHandled());
|
||||
assertTrue(handlerC.isHandled());
|
||||
handlerA.reset();
|
||||
handlerB.reset();
|
||||
handlerC.reset();
|
||||
|
||||
connectorN.getResponses("GET / HTTP/1.0\n" + "Host: www.example.com.\n\n");
|
||||
assertTrue(handlerA.isHandled());
|
||||
assertFalse(handlerB.isHandled());
|
||||
assertFalse(handlerC.isHandled());
|
||||
handlerA.reset();
|
||||
handlerB.reset();
|
||||
handlerC.reset();
|
||||
|
||||
connectorN.getResponses("GET / HTTP/1.0\n" + "Host: localhost\n\n");
|
||||
assertFalse(handlerA.isHandled());
|
||||
assertTrue(handlerB.isHandled());
|
||||
assertFalse(handlerC.isHandled());
|
||||
handlerA.reset();
|
||||
handlerB.reset();
|
||||
handlerC.reset();
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextGetContext() throws Exception
|
||||
|
|
|
@ -434,7 +434,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
if (resource!=null && resource.exists() && !resource.isDirectory())
|
||||
{
|
||||
// Tell caches that response may vary by accept-encoding
|
||||
response.setHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
|
||||
response.addHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
|
||||
|
||||
// Does the client accept gzip?
|
||||
String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString());
|
||||
|
|
|
@ -71,7 +71,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
private static final Logger LOG = Log.getLogger(ServletHolder.class);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
private int _initOrder;
|
||||
private int _initOrder = -1;
|
||||
private boolean _initOnStartup=false;
|
||||
private Map<String, String> _roleMap;
|
||||
private String _forcedPath;
|
||||
|
@ -179,7 +179,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
*/
|
||||
public void setInitOrder(int order)
|
||||
{
|
||||
_initOnStartup=order>0;
|
||||
_initOnStartup=order>=0;
|
||||
_initOrder = order;
|
||||
}
|
||||
|
||||
|
@ -329,7 +329,6 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
throws Exception
|
||||
{
|
||||
super.initialize();
|
||||
|
||||
if (_extInstance || _initOnStartup)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -130,8 +130,8 @@ public class ServletContextHandlerTest
|
|||
|
||||
holder0.setInitOrder(0);
|
||||
_server.start();
|
||||
assertEquals(2,__testServlets.get());
|
||||
assertThat(holder0.getServletInstance(),nullValue());
|
||||
assertEquals(3,__testServlets.get());
|
||||
assertThat(holder0.getServletInstance(),notNullValue(Servlet.class));
|
||||
_server.stop();
|
||||
assertEquals(0,__testServlets.get());
|
||||
|
||||
|
|
|
@ -84,15 +84,6 @@ public class IncludableGzipFilter extends GzipFilter
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setHeader(String name, String value)
|
||||
{
|
||||
super.setHeader(name, value);
|
||||
HttpServletResponse response = (HttpServletResponse)getResponse();
|
||||
if (!response.containsHeader(name))
|
||||
response.setHeader("org.eclipse.jetty.server.include." + name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -111,15 +102,6 @@ public class IncludableGzipFilter extends GzipFilter
|
|||
{
|
||||
return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setHeader(String name, String value)
|
||||
{
|
||||
super.setHeader(name, value);
|
||||
HttpServletResponse response = (HttpServletResponse)getResponse();
|
||||
if (!response.containsHeader(name))
|
||||
response.setHeader("org.eclipse.jetty.server.include." + name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -138,15 +120,6 @@ public class IncludableGzipFilter extends GzipFilter
|
|||
{
|
||||
return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel, _deflateNoWrap));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setHeader(String name, String value)
|
||||
{
|
||||
super.setHeader(name, value);
|
||||
HttpServletResponse response = (HttpServletResponse)getResponse();
|
||||
if (!response.containsHeader(name))
|
||||
response.setHeader("org.eclipse.jetty.server.include." + name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -176,6 +149,16 @@ public class IncludableGzipFilter extends GzipFilter
|
|||
if (!response.containsHeader(name))
|
||||
response.setHeader("org.eclipse.jetty.server.include."+name,value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value)
|
||||
{
|
||||
super.addHeader(name, value);
|
||||
HttpServletResponse response = (HttpServletResponse)getResponse();
|
||||
if (!response.containsHeader(name))
|
||||
setHeader(name,value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
|
||||
{
|
||||
|
|
|
@ -234,7 +234,7 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
|
|||
setHeader("Content-Encoding", _encoding);
|
||||
if (_response.containsHeader("Content-Encoding"))
|
||||
{
|
||||
setHeader("Vary",_vary);
|
||||
addHeader("Vary",_vary);
|
||||
_out=_compressedOutputStream=createStream();
|
||||
if (_out!=null)
|
||||
{
|
||||
|
@ -269,7 +269,7 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
|
|||
if (_out == null || _bOut != null)
|
||||
{
|
||||
if (sendVary)
|
||||
setHeader("Vary",_vary);
|
||||
addHeader("Vary",_vary);
|
||||
if (_wrapper.getETag()!=null)
|
||||
setHeader("ETag",_wrapper.getETag());
|
||||
|
||||
|
@ -341,6 +341,11 @@ public abstract class AbstractCompressedStream extends ServletOutputStream
|
|||
return encoding == null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
|
||||
}
|
||||
|
||||
protected void addHeader(String name,String value)
|
||||
{
|
||||
_response.addHeader(name, value);
|
||||
}
|
||||
|
||||
protected void setHeader(String name,String value)
|
||||
{
|
||||
_response.setHeader(name, value);
|
||||
|
|
|
@ -218,8 +218,7 @@ public abstract class Resource implements ResourceFactory
|
|||
{
|
||||
URL url=null;
|
||||
// Try to format as a URL?
|
||||
ClassLoader
|
||||
loader=Thread.currentThread().getContextClassLoader();
|
||||
ClassLoader loader=Thread.currentThread().getContextClassLoader();
|
||||
if (loader!=null)
|
||||
{
|
||||
try
|
||||
|
@ -251,7 +250,7 @@ public abstract class Resource implements ResourceFactory
|
|||
{
|
||||
url=ClassLoader.getSystemResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=loader.getResource(resource.substring(1));
|
||||
url=ClassLoader.getSystemResource(resource.substring(1));
|
||||
}
|
||||
|
||||
if (url==null)
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class QueueBenchmarkTest
|
||||
{
|
||||
private static final Logger logger = Log.getLogger(QueueBenchmarkTest.class);
|
||||
private static final Runnable ELEMENT = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
}
|
||||
};
|
||||
private static final Runnable END = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void testQueues() throws Exception
|
||||
{
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
Assume.assumeTrue(cores > 1);
|
||||
|
||||
final int readers = cores / 2;
|
||||
final int writers = readers;
|
||||
final int iterations = 16 * 1024 * 1024;
|
||||
|
||||
final List<Queue<Runnable>> queues = new ArrayList<>();
|
||||
queues.add(new ConcurrentArrayQueue<Runnable>()); // Jetty lock-free queue, allocating array blocks
|
||||
queues.add(new ConcurrentLinkedQueue<Runnable>()); // JDK lock-free queue, allocating nodes
|
||||
queues.add(new ArrayBlockingQueue<Runnable>(iterations * writers)); // JDK lock-based, circular array queue
|
||||
queues.add(new BlockingArrayQueue<Runnable>(iterations * writers)); // Jetty lock-based, circular array queue
|
||||
|
||||
testQueues(readers, writers, iterations, queues, false);
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void testBlockingQueues() throws Exception
|
||||
{
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
Assume.assumeTrue(cores > 1);
|
||||
|
||||
final int readers = cores / 2;
|
||||
final int writers = readers;
|
||||
final int iterations = 16 * 1024 * 1024;
|
||||
|
||||
final List<Queue<Runnable>> queues = new ArrayList<>();
|
||||
queues.add(new ConcurrentArrayBlockingQueue.Unbounded<Runnable>());
|
||||
queues.add(new ConcurrentArrayBlockingQueue.Bounded<Runnable>(iterations * writers));
|
||||
queues.add(new LinkedBlockingQueue<Runnable>());
|
||||
queues.add(new ArrayBlockingQueue<Runnable>(iterations * writers));
|
||||
queues.add(new BlockingArrayQueue<Runnable>(iterations * writers));
|
||||
|
||||
testQueues(readers, writers, iterations, queues, true);
|
||||
}
|
||||
|
||||
private void testQueues(final int readers, final int writers, final int iterations, List<Queue<Runnable>> queues, final boolean blocking) throws Exception
|
||||
{
|
||||
final int runs = 8;
|
||||
int threads = readers + writers;
|
||||
final CyclicBarrier barrier = new CyclicBarrier(threads + 1);
|
||||
|
||||
for (final Queue<Runnable> queue : queues)
|
||||
{
|
||||
for (int r = 0; r < runs; ++r)
|
||||
{
|
||||
for (int i = 0; i < readers; ++i)
|
||||
{
|
||||
Thread thread = new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
await(barrier);
|
||||
consume(queue, writers, blocking);
|
||||
await(barrier);
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
}
|
||||
for (int i = 0; i < writers; ++i)
|
||||
{
|
||||
Thread thread = new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
await(barrier);
|
||||
produce(queue, readers, iterations);
|
||||
await(barrier);
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
}
|
||||
|
||||
await(barrier);
|
||||
long begin = System.nanoTime();
|
||||
await(barrier);
|
||||
long end = System.nanoTime();
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
|
||||
logger.info("{} Readers/Writers: {}/{} => {} ms", queue.getClass().getSimpleName(), readers, writers, elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void consume(Queue<Runnable> queue, int writers, boolean blocking)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Runnable element = blocking ? take(queue) : poll(queue);
|
||||
if (element == END)
|
||||
if (--writers == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void produce(Queue<Runnable> queue, int readers, int iterations)
|
||||
{
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
append(queue, ELEMENT);
|
||||
for (int i = 0; i < readers; ++i)
|
||||
append(queue, END);
|
||||
}
|
||||
|
||||
private static void append(Queue<Runnable> queue, Runnable element)
|
||||
{
|
||||
if (!queue.offer(element))
|
||||
logger.warn("Queue {} capacity is too small", queue);
|
||||
}
|
||||
|
||||
private static Runnable take(Queue<Runnable> queue)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((BlockingQueue<Runnable>)queue).take();
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static Runnable poll(Queue<Runnable> queue)
|
||||
{
|
||||
int loops = 0;
|
||||
while (true)
|
||||
{
|
||||
Runnable element = queue.poll();
|
||||
if (element != null)
|
||||
return element;
|
||||
// Busy loop
|
||||
sleepMicros(1);
|
||||
++loops;
|
||||
if (loops % 16 == 0)
|
||||
logger.warn("Spin looping while polling empty queue: {} spins: ", loops);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sleepMicros(long sleep)
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeUnit.MICROSECONDS.sleep(sleep);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void await(CyclicBarrier barrier)
|
||||
{
|
||||
try
|
||||
{
|
||||
barrier.await();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
}
|
4
pom.xml
4
pom.xml
|
@ -284,7 +284,7 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-showversion -XX:+PrintGCDetails</argLine>
|
||||
<argLine>-showversion -Xmx1g -Xms1g -XX:+PrintGCDetails</argLine>
|
||||
<failIfNoTests>false</failIfNoTests>
|
||||
<!--systemProperties>
|
||||
<property>
|
||||
|
@ -702,7 +702,7 @@
|
|||
})();
|
||||
</script>
|
||||
]]>
|
||||
</header>
|
||||
</header>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
|
Loading…
Reference in New Issue