Merged branch 'jetty-10.0.x' into 'jetty-10.0.x-4400-review_httpclient_content'.

This commit is contained in:
Simone Bordet 2020-03-23 16:24:04 +01:00
commit e215d071c8
186 changed files with 2884 additions and 1552 deletions

2
Jenkinsfile vendored
View File

@ -55,7 +55,7 @@ pipeline {
agent { node { label 'linux' } } agent { node { label 'linux' } }
steps { steps {
timeout(time: 30, unit: 'MINUTES') { timeout(time: 30, unit: 'MINUTES') {
mavenBuild("jdk11", "install javadoc:javadoc -DskipTests -Dpmd.skip=true -Dcheckstyle.skip=true", "maven3", true) mavenBuild("jdk11", "package source:jar javadoc:jar javadoc:aggregate-jar -Peclipse-release -DskipTests -Dpmd.skip=true -Dcheckstyle.skip=true", "maven3", true)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'JavaDoc'], [parserName: 'Java']] warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'JavaDoc'], [parserName: 'Java']]
} }
} }

View File

@ -85,10 +85,6 @@
<artifactId>jetty-annotations</artifactId> <artifactId>jetty-annotations</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
@ -112,7 +108,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -20,6 +20,18 @@
<targetPath>META-INF</targetPath> <targetPath>META-INF</targetPath>
</resource> </resource>
</resources> </resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<!-- No point building javadoc for this project -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@ -16,6 +16,15 @@
</build> </build>
<dependencies> <dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.example-async-rest</groupId> <groupId>org.eclipse.jetty.example-async-rest</groupId>
<artifactId>example-async-rest-jar</artifactId> <artifactId>example-async-rest-jar</artifactId>

View File

@ -22,6 +22,7 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -58,7 +58,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -40,7 +40,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -30,7 +30,15 @@
<artifactId>jetty-io</artifactId> <artifactId>jetty-io</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId> <artifactId>http2-server</artifactId>

View File

@ -54,7 +54,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -51,6 +51,15 @@
<artifactId>jetty-alpn-server</artifactId> <artifactId>jetty-alpn-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.http2</groupId> <groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId> <artifactId>http2-server</artifactId>

View File

@ -44,6 +44,15 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId> <artifactId>jetty-test-helper</artifactId>

View File

@ -69,6 +69,10 @@
<groupId>org.ow2.asm</groupId> <groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId> <artifactId>asm-commons</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>jakarta.transaction</groupId> <groupId>jakarta.transaction</groupId>
@ -86,5 +90,10 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -78,5 +78,14 @@
<artifactId>jetty-annotations</artifactId> <artifactId>jetty-annotations</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -32,6 +32,15 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

View File

@ -146,6 +146,19 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
} }
} }
@Override
public boolean accept(Connection connection)
{
while (true)
{
int count = connections.getLo();
if (count >= maxConnections)
return false;
if (connections.compareAndSetLo(count, count + 1))
return true;
}
}
protected abstract void onCreated(Connection connection); protected abstract void onCreated(Connection connection);
protected void proceed() protected void proceed()

View File

@ -52,6 +52,14 @@ public interface ConnectionPool extends Closeable
*/ */
Connection acquire(); Connection acquire();
/**
* <p>Accepts the given connection to be managed by this ConnectionPool.</p>
*
* @param connection the connection to accept
* @return whether the connection has been accepted
*/
boolean accept(Connection connection);
/** /**
* <p>Returns the given connection, previously obtained via {@link #acquire()}, * <p>Returns the given connection, previously obtained via {@link #acquire()},
* back to this ConnectionPool.</p> * back to this ConnectionPool.</p>

View File

@ -216,6 +216,7 @@ public class HttpClient extends ContainerLifeCycle
handlers.put(new RedirectProtocolHandler(this)); handlers.put(new RedirectProtocolHandler(this));
handlers.put(new WWWAuthenticationProtocolHandler(this)); handlers.put(new WWWAuthenticationProtocolHandler(this));
handlers.put(new ProxyAuthenticationProtocolHandler(this)); handlers.put(new ProxyAuthenticationProtocolHandler(this));
handlers.put(new UpgradeProtocolHandler());
decoderFactories.add(new GZIPContentDecoder.Factory(byteBufferPool)); decoderFactories.add(new GZIPContentDecoder.Factory(byteBufferPool));
@ -523,7 +524,7 @@ public class HttpClient extends ContainerLifeCycle
return new Origin(scheme, host, port, request.getTag(), protocol); return new Origin(scheme, host, port, request.getTag(), protocol);
} }
HttpDestination resolveDestination(Origin origin) public HttpDestination resolveDestination(Origin origin)
{ {
return destinations.computeIfAbsent(origin, o -> return destinations.computeIfAbsent(origin, o ->
{ {

View File

@ -80,6 +80,11 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
this.timeout = new TimeoutTask(client.getScheduler()); this.timeout = new TimeoutTask(client.getScheduler());
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
ProxyConfiguration proxyConfig = client.getProxyConfiguration(); ProxyConfiguration proxyConfig = client.getProxyConfiguration();
proxy = proxyConfig.match(origin); proxy = proxyConfig.match(origin);
ClientConnectionFactory connectionFactory = client.getTransport(); ClientConnectionFactory connectionFactory = client.getTransport();
@ -98,11 +103,11 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (tag instanceof ClientConnectionFactory.Decorator) if (tag instanceof ClientConnectionFactory.Decorator)
connectionFactory = ((ClientConnectionFactory.Decorator)tag).apply(connectionFactory); connectionFactory = ((ClientConnectionFactory.Decorator)tag).apply(connectionFactory);
this.connectionFactory = connectionFactory; this.connectionFactory = connectionFactory;
}
String host = HostPort.normalizeHost(getHost()); public void accept(Connection connection)
if (!client.isDefaultPort(getScheme(), getPort())) {
host += ":" + getPort(); connectionPool.accept(connection);
hostField = new HttpField(HttpHeader.HOST, host);
} }
@Override @Override
@ -497,7 +502,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
{ {
return String.format("%s[%s]@%x%s,queue=%d,pool=%s", return String.format("%s[%s]@%x%s,queue=%d,pool=%s",
HttpDestination.class.getSimpleName(), HttpDestination.class.getSimpleName(),
asString(), getOrigin(),
hashCode(), hashCode(),
proxy == null ? "" : "(via " + proxy + ")", proxy == null ? "" : "(via " + proxy + ")",
exchanges.size(), exchanges.size(),

View File

@ -50,6 +50,11 @@ public class HttpExchange
conversation.updateResponseListeners(null); conversation.updateResponseListeners(null);
} }
public HttpDestination getHttpDestination()
{
return destination;
}
public HttpConversation getConversation() public HttpConversation getConversation()
{ {
return request.getConversation(); return request.getConversation();

View File

@ -18,15 +18,48 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
/**
* <p>HttpUpgrader prepares a HTTP request to upgrade from one protocol to another,
* and implements the upgrade mechanism.</p>
* <p>The upgrade mechanism can be the
* <a href="https://tools.ietf.org/html/rfc7230#section-6.7">HTTP/1.1 upgrade mechanism</a>
* or the
* <a href="https://tools.ietf.org/html/rfc8441#section-4">HTTP/2 extended CONNECT mechanism</a>.</p>
* <p>Given the differences among mechanism implementations, a request needs to be
* prepared before being sent to comply with the mechanism requirements (for example,
* add required headers, etc.).</p>
*/
public interface HttpUpgrader public interface HttpUpgrader
{ {
/**
* <p>Prepares the request for the upgrade, for example by setting the HTTP method
* or by setting HTTP headers required for the upgrade.</p>
*
* @param request the request to prepare
*/
public void prepare(HttpRequest request); public void prepare(HttpRequest request);
public void upgrade(HttpResponse response, EndPoint endPoint); /**
* <p>Upgrades the given {@code endPoint} to a different protocol.</p>
* <p>The success or failure of the upgrade should be communicated via the given {@code callback}.</p>
* <p>An exception thrown by this method is equivalent to failing the callback.</p>
*
* @param response the response with the information about the upgrade
* @param endPoint the EndPoint to upgrade
* @param callback a callback to notify of the success or failure of the upgrade
*/
public void upgrade(HttpResponse response, EndPoint endPoint, Callback callback);
/**
* <p>A factory for {@link HttpUpgrader}s.</p>
* <p>A {@link Request} subclass should implement this interface
* if it wants to create a specific HttpUpgrader.</p>
*/
public interface Factory public interface Factory
{ {
public HttpUpgrader newHttpUpgrader(HttpVersion version); public HttpUpgrader newHttpUpgrader(HttpVersion version);

View File

@ -85,6 +85,25 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
} }
} }
@Override
public boolean accept(Connection connection)
{
boolean accepted = super.accept(connection);
if (LOG.isDebugEnabled())
LOG.debug("Accepted {} {}", accepted, connection);
if (accepted)
{
synchronized (this)
{
Holder holder = new Holder(connection);
activeConnections.put(connection, holder);
++holder.count;
}
active(connection);
}
return accepted;
}
@Override @Override
public boolean isActive(Connection connection) public boolean isActive(Connection connection)
{ {

View File

@ -0,0 +1,110 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.client;
import java.util.List;
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.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
/**
* <p>A protocol handler that handles HTTP 101 responses.</p>
*/
public class UpgradeProtocolHandler implements ProtocolHandler
{
private final List<String> protocols = List.of("websocket", "h2c");
@Override
public String getName()
{
return "upgrade";
}
@Override
public boolean accept(Request request, Response response)
{
boolean upgraded = HttpStatus.SWITCHING_PROTOCOLS_101 == response.getStatus();
boolean accepted = false;
if (upgraded)
accepted = acceptHeaders(request, response);
return upgraded && accepted;
}
protected boolean acceptHeaders(Request request, Response response)
{
HttpField responseUpgrade = response.getHeaders().getField(HttpHeader.UPGRADE);
if (responseUpgrade != null && protocols.stream().anyMatch(responseUpgrade::contains))
return true;
// The response may not contain the Upgrade header, so check the request.
HttpField requestUpgrade = request.getHeaders().getField(HttpHeader.UPGRADE);
return requestUpgrade != null && protocols.stream().anyMatch(requestUpgrade::contains);
}
@Override
public Response.Listener getResponseListener()
{
return new Response.Listener.Adapter()
{
@Override
public void onComplete(Result result)
{
HttpResponse response = (HttpResponse)result.getResponse();
HttpRequest request = (HttpRequest)response.getRequest();
if (result.isSucceeded())
{
try
{
HttpConversation conversation = request.getConversation();
HttpUpgrader upgrader = (HttpUpgrader)conversation.getAttribute(HttpUpgrader.class.getName());
if (upgrader == null)
throw new HttpResponseException("101 response without " + HttpUpgrader.class.getSimpleName(), response);
EndPoint endPoint = (EndPoint)conversation.getAttribute(EndPoint.class.getName());
if (endPoint == null)
throw new HttpResponseException("Upgrade without " + EndPoint.class.getSimpleName(), response);
upgrader.upgrade(response, endPoint, Callback.from(Callback.NOOP::succeeded, x -> forwardFailureComplete(request, null, response, x)));
}
catch (Throwable x)
{
forwardFailureComplete(request, null, response, x);
}
}
else
{
forwardFailureComplete(request, result.getRequestFailure(), response, result.getResponseFailure());
}
}
};
}
private void forwardFailureComplete(HttpRequest request, Throwable requestFailure, Response response, Throwable responseFailure)
{
HttpConversation conversation = request.getConversation();
conversation.updateResponseListeners(null);
List<Response.ResponseListener> responseListeners = conversation.getResponseListeners();
ResponseNotifier notifier = new ResponseNotifier();
notifier.forwardFailure(responseListeners, response, responseFailure);
notifier.notifyComplete(responseListeners, new Result(request, requestFailure, response, responseFailure));
}
}

View File

@ -109,10 +109,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
.distinct() .distinct()
.map(p -> p.toLowerCase(Locale.ENGLISH)) .map(p -> p.toLowerCase(Locale.ENGLISH))
.collect(Collectors.toList()); .collect(Collectors.toList());
for (ClientConnectionFactory.Info factoryInfo : factoryInfos) Arrays.stream(factoryInfos).forEach(this::addBean);
{
addBean(factoryInfo);
}
setConnectionPoolFactory(destination -> setConnectionPoolFactory(destination ->
new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1)); new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1));
} }
@ -133,13 +130,22 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
} }
else else
{ {
// Preserve the order of protocols chosen by the application. if (ssl)
// We need to keep multiple protocols in case the protocol {
// is negotiated: e.g. [http/1.1, h2] negotiates [h2], but // There may be protocol negotiation, so preserve the order
// here we don't know yet what will be negotiated. // of protocols chosen by the application.
protocols = this.protocols.stream() // We need to keep multiple protocols in case the protocol
.filter(p -> p.equals(http1) || p.equals(http2)) // is negotiated: e.g. [http/1.1, h2] negotiates [h2], but
.collect(Collectors.toList()); // here we don't know yet what will be negotiated.
protocols = this.protocols.stream()
.filter(p -> p.equals(http1) || p.equals(http2))
.collect(Collectors.toList());
}
else
{
// Pick the first.
protocols = List.of(this.protocols.get(0));
}
} }
Origin.Protocol protocol = null; Origin.Protocol protocol = null;
if (!protocols.isEmpty()) if (!protocols.isEmpty())
@ -179,6 +185,15 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context); return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
} }
public void upgrade(EndPoint endPoint, Map<String, Object> context)
{
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
Origin.Protocol protocol = destination.getOrigin().getProtocol();
Info info = findClientConnectionFactoryInfo(protocol.getProtocols())
.orElseThrow(() -> new IllegalStateException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " to upgrade to " + protocol));
info.upgrade(endPoint, context);
}
protected Connection newNegotiatedConnection(EndPoint endPoint, Map<String, Object> context) throws IOException protected Connection newNegotiatedConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{ {
try try

View File

@ -18,15 +18,10 @@
package org.eclipse.jetty.client.http; package org.eclipse.jetty.client.http;
import java.util.Locale;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import org.eclipse.jetty.client.HttpChannel; import org.eclipse.jetty.client.HttpChannel;
import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponse;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.HttpUpgrader;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
@ -92,37 +87,6 @@ public class HttpChannelOverHTTP extends HttpChannel
connection.release(); connection.release();
} }
@Override
public Result exchangeTerminating(HttpExchange exchange, Result result)
{
if (result.isFailed())
return result;
HttpResponse response = exchange.getResponse();
if (response.getVersion() == HttpVersion.HTTP_1_1 && response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
{
String header = response.getHeaders().get(HttpHeader.CONNECTION);
if (header == null || !header.toLowerCase(Locale.US).contains("upgrade"))
return new Result(result, new HttpResponseException("101 response without 'Connection: Upgrade'", response));
HttpRequest request = exchange.getRequest();
HttpUpgrader upgrader = (HttpUpgrader)request.getConversation().getAttribute(HttpUpgrader.class.getName());
if (upgrader == null)
return new Result(result, new HttpResponseException("101 response without " + HttpUpgrader.class.getSimpleName(), response));
try
{
upgrader.upgrade(response, getHttpConnection().getEndPoint());
}
catch (Throwable x)
{
return new Result(result, new HttpResponseException("Could not upgrade to WebSocket", response, x));
}
}
return result;
}
public void receive() public void receive()
{ {
inMessages.increment(); inMessages.increment();

View File

@ -29,6 +29,7 @@ import java.util.concurrent.atomic.LongAdder;
import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpConnection; import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpConversation;
import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.HttpProxy;
@ -39,6 +40,7 @@ import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
@ -277,17 +279,35 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements IConne
protected void normalizeRequest(Request request) protected void normalizeRequest(Request request)
{ {
super.normalizeRequest(request); super.normalizeRequest(request);
if (request instanceof HttpProxy.TunnelRequest) if (request instanceof HttpProxy.TunnelRequest)
{ {
long connectTimeout = getHttpClient().getConnectTimeout(); long connectTimeout = getHttpClient().getConnectTimeout();
request.timeout(connectTimeout, TimeUnit.MILLISECONDS) request.timeout(connectTimeout, TimeUnit.MILLISECONDS)
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS); .idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS);
} }
if (request instanceof HttpUpgrader.Factory)
HttpRequest httpRequest = (HttpRequest)request;
HttpConversation conversation = httpRequest.getConversation();
HttpUpgrader upgrader = (HttpUpgrader)conversation.getAttribute(HttpUpgrader.class.getName());
if (upgrader == null)
{ {
HttpUpgrader upgrader = ((HttpUpgrader.Factory)request).newHttpUpgrader(HttpVersion.HTTP_1_1); if (request instanceof HttpUpgrader.Factory)
((HttpRequest)request).getConversation().setAttribute(HttpUpgrader.class.getName(), upgrader); {
upgrader.prepare((HttpRequest)request); upgrader = ((HttpUpgrader.Factory)request).newHttpUpgrader(HttpVersion.HTTP_1_1);
conversation.setAttribute(HttpUpgrader.class.getName(), upgrader);
upgrader.prepare(httpRequest);
}
else
{
String protocol = request.getHeaders().get(HttpHeader.UPGRADE);
if (protocol != null)
{
upgrader = new ProtocolHttpUpgrader(getHttpDestination(), protocol);
conversation.setAttribute(HttpUpgrader.class.getName(), upgrader);
upgrader.prepare(httpRequest);
}
}
} }
} }

View File

@ -1,26 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.client.http;
import org.eclipse.jetty.client.HttpResponse;
public interface HttpConnectionUpgrader
{
public void upgrade(HttpResponse response, HttpConnectionOverHTTP connection);
}

View File

@ -285,12 +285,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
if (exchange == null) if (exchange == null)
return false; return false;
if (HttpMethod.CONNECT.is(exchange.getRequest().getMethod())) // Store the EndPoint is case of upgrades, tunnels, etc.
{ exchange.getRequest().getConversation().setAttribute(EndPoint.class.getName(), getHttpConnection().getEndPoint());
// Store the EndPoint even in case of non-200 responses.
exchange.getRequest().getConversation().setAttribute(EndPoint.class.getName(), getHttpConnection().getEndPoint());
}
return !responseHeaders(exchange); return !responseHeaders(exchange);
} }

View File

@ -34,12 +34,12 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.log.Log; import org.slf4j.Logger;
import org.eclipse.jetty.util.log.Logger; import org.slf4j.LoggerFactory;
public class HttpSenderOverHTTP extends HttpSender public class HttpSenderOverHTTP extends HttpSender
{ {
private static final Logger LOG = Log.getLogger(HttpSenderOverHTTP.class); private static final Logger LOG = LoggerFactory.getLogger(HttpSenderOverHTTP.class);
private final IteratingCallback headersCallback = new HeadersCallback(); private final IteratingCallback headersCallback = new HeadersCallback();
private final IteratingCallback contentCallback = new ContentCallback(); private final IteratingCallback contentCallback = new ContentCallback();

View File

@ -0,0 +1,101 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.client.http;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponse;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.HttpUpgrader;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>A HttpUpgrader that upgrades to a given protocol.</p>
* <p>Works in conjunction with {@link HttpClientTransportDynamic}
* so that the protocol to upgrade to must be one of the application
* protocols supported by HttpClientTransportDynamic.</p>
* <p></p>
*/
public class ProtocolHttpUpgrader implements HttpUpgrader
{
private static final Logger LOG = LoggerFactory.getLogger(ProtocolHttpUpgrader.class);
private final HttpDestination destination;
private final String protocol;
public ProtocolHttpUpgrader(HttpDestination destination, String protocol)
{
this.destination = destination;
this.protocol = protocol;
}
@Override
public void prepare(HttpRequest request)
{
}
@Override
public void upgrade(HttpResponse response, EndPoint endPoint, Callback callback)
{
if (response.getHeaders().contains(HttpHeader.UPGRADE, protocol))
{
HttpClient httpClient = destination.getHttpClient();
HttpClientTransport transport = httpClient.getTransport();
if (transport instanceof HttpClientTransportDynamic)
{
HttpClientTransportDynamic dynamicTransport = (HttpClientTransportDynamic)transport;
Origin origin = destination.getOrigin();
Origin newOrigin = new Origin(origin.getScheme(), origin.getAddress(), origin.getTag(), new Origin.Protocol(List.of(protocol), false));
HttpDestination newDestination = httpClient.resolveDestination(newOrigin);
Map<String, Object> context = new HashMap<>();
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, newDestination);
context.put(HttpResponse.class.getName(), response);
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, Promise.from(y -> callback.succeeded(), callback::failed));
if (LOG.isDebugEnabled())
LOG.debug("Upgrading {} on {}", response.getRequest(), endPoint);
dynamicTransport.upgrade(endPoint, context);
}
else
{
callback.failed(new HttpResponseException(HttpClientTransportDynamic.class.getName() + " required to upgrade to: " + protocol, response));
}
}
else
{
callback.failed(new HttpResponseException("Not an upgrade to: " + protocol, response));
}
}
}

View File

@ -29,16 +29,16 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* <p>Implements the conversion from {@link ContentProvider} to {@link Request.Content}.</p> * <p>Implements the conversion from {@link ContentProvider} to {@link Request.Content}.</p>
*/ */
public class RequestContentAdapter implements Request.Content, Request.Content.Subscription, AsyncContentProvider.Listener, Callback public class RequestContentAdapter implements Request.Content, Request.Content.Subscription, AsyncContentProvider.Listener, Callback
{ {
private static final Logger LOG = Log.getLogger(RequestContentAdapter.class); private static final Logger LOG = LoggerFactory.getLogger(RequestContentAdapter.class);
private final AutoLock lock = new AutoLock(); private final AutoLock lock = new AutoLock();
private final ContentProvider provider; private final ContentProvider provider;
@ -310,7 +310,7 @@ public class RequestContentAdapter implements Request.Content, Request.Content.S
} }
catch (Exception x) catch (Exception x)
{ {
LOG.ignore(x); LOG.trace("Failure while notifying content failure {}", failure, x);
} }
} }

View File

@ -24,13 +24,13 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractRequestContent implements Request.Content public abstract class AbstractRequestContent implements Request.Content
{ {
private static final Logger LOG = Log.getLogger(AbstractRequestContent.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractRequestContent.class);
private final AutoLock lock = new AutoLock(); private final AutoLock lock = new AutoLock();
private final String contentType; private final String contentType;
@ -223,7 +223,7 @@ public abstract class AbstractRequestContent implements Request.Content
} }
catch (Exception x) catch (Exception x)
{ {
LOG.ignore(x); LOG.trace("Failure while notifying content failure {}", failure, x);
} }
} }

View File

@ -34,13 +34,13 @@ import java.util.stream.Stream;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AsyncRequestContent implements Request.Content, Request.Content.Subscription, Closeable public class AsyncRequestContent implements Request.Content, Request.Content.Subscription, Closeable
{ {
private static final Logger LOG = Log.getLogger(AsyncRequestContent.class); private static final Logger LOG = LoggerFactory.getLogger(AsyncRequestContent.class);
private final AutoLock lock = new AutoLock(); private final AutoLock lock = new AutoLock();
private final Condition flush = lock.newCondition(); private final Condition flush = lock.newCondition();
@ -287,7 +287,7 @@ public class AsyncRequestContent implements Request.Content, Request.Content.Sub
} }
catch (Throwable x) catch (Throwable x)
{ {
LOG.ignore(x); LOG.trace("Failure while notifying content failure {}", failure, x);
} }
} }

View File

@ -34,8 +34,8 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log; import org.slf4j.Logger;
import org.eclipse.jetty.util.log.Logger; import org.slf4j.LoggerFactory;
/** /**
* <p>A {@link Request.Content} for form uploads with the {@code "multipart/form-data"} * <p>A {@link Request.Content} for form uploads with the {@code "multipart/form-data"}
@ -61,7 +61,7 @@ import org.eclipse.jetty.util.log.Logger;
*/ */
public class MultiPartRequestContent extends AbstractRequestContent implements Closeable public class MultiPartRequestContent extends AbstractRequestContent implements Closeable
{ {
private static final Logger LOG = Log.getLogger(MultiPartRequestContent.class); private static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestContent.class);
private static final byte[] COLON_SPACE_BYTES = new byte[]{':', ' '}; private static final byte[] COLON_SPACE_BYTES = new byte[]{':', ' '};
private static final byte[] CR_LF_BYTES = new byte[]{'\r', '\n'}; private static final byte[] CR_LF_BYTES = new byte[]{'\r', '\n'};

View File

@ -33,8 +33,8 @@ import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log; import org.slf4j.Logger;
import org.eclipse.jetty.util.log.Logger; import org.slf4j.LoggerFactory;
/** /**
* <p>A {@link Request.Content} for files using JDK 7's {@code java.nio.file} APIs.</p> * <p>A {@link Request.Content} for files using JDK 7's {@code java.nio.file} APIs.</p>
@ -46,7 +46,7 @@ import org.eclipse.jetty.util.log.Logger;
*/ */
public class PathRequestContent extends AbstractRequestContent public class PathRequestContent extends AbstractRequestContent
{ {
private static final Logger LOG = Log.getLogger(PathRequestContent.class); private static final Logger LOG = LoggerFactory.getLogger(PathRequestContent.class);
private final Path filePath; private final Path filePath;
private final long fileSize; private final long fileSize;

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.client.http;
import java.io.EOFException; import java.io.EOFException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -96,7 +96,7 @@ public class HttpReceiverOverHTTPTest
{ {
HttpRequest request = (HttpRequest)client.newRequest("http://localhost"); HttpRequest request = (HttpRequest)client.newRequest("http://localhost");
FutureResponseListener listener = new FutureResponseListener(request); FutureResponseListener listener = new FutureResponseListener(request);
HttpExchange exchange = new HttpExchange(destination, request, Collections.<Response.ResponseListener>singletonList(listener)); HttpExchange exchange = new HttpExchange(destination, request, List.of(listener));
boolean associated = connection.getHttpChannel().associate(exchange); boolean associated = connection.getHttpChannel().associate(exchange);
assertTrue(associated); assertTrue(associated);
exchange.requestComplete(null); exchange.requestComplete(null);

View File

@ -55,15 +55,9 @@
<version>${project.version}</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -42,7 +42,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -39,15 +39,9 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -16,6 +16,10 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.google.cloud</groupId> <groupId>com.google.cloud</groupId>
<artifactId>google-cloud-datastore</artifactId> <artifactId>google-cloud-datastore</artifactId>
@ -80,6 +84,11 @@
<artifactId>jetty-test-helper</artifactId> <artifactId>jetty-test-helper</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<properties> <properties>

View File

@ -38,6 +38,15 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>

View File

@ -633,7 +633,7 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>

View File

@ -35,6 +35,22 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>javax.xml</groupId> <groupId>javax.xml</groupId>
<artifactId>jaxws-api</artifactId> <artifactId>jaxws-api</artifactId>

View File

@ -16,23 +16,19 @@
// ======================================================================== // ========================================================================
// //
package org.eclipse.jetty.websocket.common.endpoints.annotated; package org.eclipse.jetty.http.spi;
import org.eclipse.jetty.websocket.api.Frame; public final class LoggingUtil
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
@WebSocket
public class FrameSocket
{ {
/** /**
* A frame * It's easier to setup logging in code for this test project,
* * then it is to setup the various system properties and files for every test
* @param frame the frame * execution (maven, CI, and IDE).
*/ */
@OnWebSocketFrame public static void init()
public void frameMe(Frame frame)
{ {
/* ignore */ // Wire up java.util.logging (used by javax.xml.soap others) to slf4j.
org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger();
org.slf4j.bridge.SLF4JBridgeHandler.install();
} }
} }

View File

@ -44,6 +44,11 @@ import static org.hamcrest.Matchers.is;
public class SPIServerTest public class SPIServerTest
{ {
static
{
LoggingUtil.init();
}
String host = "localhost"; String host = "localhost";
HttpServer server; HttpServer server;
int port; int port;

View File

@ -41,6 +41,10 @@ import org.junit.jupiter.api.Test;
public class TestEndpointMultiplePublishProblem public class TestEndpointMultiplePublishProblem
{ {
static
{
LoggingUtil.init();
}
private static String default_impl = System.getProperty("com.sun.net.httpserver.HttpServerProvider"); private static String default_impl = System.getProperty("com.sun.net.httpserver.HttpServerProvider");

View File

@ -42,6 +42,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestSPIServer public class TestSPIServer
{ {
static
{
LoggingUtil.init();
}
/** /**
* Create a server that has a null InetSocketAddress, then * Create a server that has a null InetSocketAddress, then

View File

@ -0,0 +1,5 @@
# Jetty Logging using jetty-slf4j-impl
org.eclipse.jetty.logging.appender.NAME_CONDENSE=false
org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false
# org.eclipse.jetty.LEVEL=WARN
log.LEVEL=INFO

View File

@ -25,11 +25,14 @@
<artifactId>jetty-io</artifactId> <artifactId>jetty-io</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -47,7 +47,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -30,7 +30,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -29,6 +29,7 @@ import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.parser.Parser; import org.eclipse.jetty.http2.parser.Parser;
import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBuffer;
import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.io.WriteFlusher;
@ -41,7 +42,7 @@ import org.eclipse.jetty.util.thread.strategy.EatWhatYouKill;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class HTTP2Connection extends AbstractConnection implements WriteFlusher.Listener public class HTTP2Connection extends AbstractConnection implements WriteFlusher.Listener, Connection.UpgradeTo
{ {
protected static final Logger LOG = LoggerFactory.getLogger(HTTP2Connection.class); protected static final Logger LOG = LoggerFactory.getLogger(HTTP2Connection.class);
@ -95,8 +96,11 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
return parser; return parser;
} }
protected void setInputBuffer(ByteBuffer buffer) @Override
public void onUpgradeTo(ByteBuffer buffer)
{ {
if (LOG.isDebugEnabled())
LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
if (buffer != null) if (buffer != null)
producer.setInputBuffer(buffer); producer.setInputBuffer(buffer);
} }

View File

@ -540,19 +540,10 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
boolean queued; boolean queued;
synchronized (this) synchronized (this)
{ {
int streamId = frame.getStreamId(); HeadersFrame[] frameOut = new HeadersFrame[1];
if (streamId <= 0) stream = newStream(frame, frameOut);
{
streamId = localStreamIds.getAndAdd(2);
PriorityFrame priority = frame.getPriority();
priority = priority == null ? null : new PriorityFrame(streamId, priority.getParentStreamId(),
priority.getWeight(), priority.isExclusive());
frame = new HeadersFrame(streamId, frame.getMetaData(), priority, frame.isEndStream());
}
stream = createLocalStream(streamId, (MetaData.Request)frame.getMetaData());
stream.setListener(listener); stream.setListener(listener);
ControlEntry entry = new ControlEntry(frameOut[0], stream, new StreamPromiseCallback(promise, stream));
ControlEntry entry = new ControlEntry(frame, stream, new StreamPromiseCallback(promise, stream));
queued = flusher.append(entry); queued = flusher.append(entry);
} }
stream.process(new PrefaceFrame(), Callback.NOOP); stream.process(new PrefaceFrame(), Callback.NOOP);
@ -566,6 +557,33 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
} }
} }
/**
* <p>Creates a new stream allocating a stream id if the given HEADERS frame does not have one.</p>
* <p>The new HEADERS frame with the newly allocated stream id is returned as the first element
* of the array parameter.</p>
*
* @param frameIn the HEADERS frame that triggered the stream creation
* @param frameOut an array of size 1 to return the HEADERS frame with the newly
* allocated stream id, or null if not interested in the modified headers frame
* @return a new stream
*/
public IStream newStream(HeadersFrame frameIn, HeadersFrame[] frameOut)
{
HeadersFrame frame = frameIn;
int streamId = frameIn.getStreamId();
if (streamId <= 0)
{
streamId = localStreamIds.getAndAdd(2);
PriorityFrame priority = frameIn.getPriority();
priority = priority == null ? null : new PriorityFrame(streamId, priority.getParentStreamId(),
priority.getWeight(), priority.isExclusive());
frame = new HeadersFrame(streamId, frameIn.getMetaData(), priority, frameIn.isEndStream());
}
if (frameOut != null)
frameOut[0] = frame;
return createLocalStream(streamId, (MetaData.Request)frame.getMetaData());
}
protected IStream newStream(int streamId, MetaData.Request request, boolean local) protected IStream newStream(int streamId, MetaData.Request request, boolean local)
{ {
return new HTTP2Stream(scheduler, this, streamId, request, local); return new HTTP2Stream(scheduler, this, streamId, request, local);

View File

@ -62,7 +62,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -44,7 +44,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -19,16 +19,23 @@
package org.eclipse.jetty.http2.client.http; package org.eclipse.jetty.http2.client.http;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.http2.client.HTTP2Client; import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory; import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
public class ClientConnectionFactoryOverHTTP2 implements ClientConnectionFactory public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle implements ClientConnectionFactory
{ {
private final ClientConnectionFactory factory = new HTTP2ClientConnectionFactory(); private final ClientConnectionFactory factory = new HTTP2ClientConnectionFactory();
private final HTTP2Client client; private final HTTP2Client client;
@ -36,10 +43,11 @@ public class ClientConnectionFactoryOverHTTP2 implements ClientConnectionFactory
public ClientConnectionFactoryOverHTTP2(HTTP2Client client) public ClientConnectionFactoryOverHTTP2(HTTP2Client client)
{ {
this.client = client; this.client = client;
addBean(client);
} }
@Override @Override
public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{ {
HTTPSessionListenerPromise listenerPromise = new HTTPSessionListenerPromise(context); HTTPSessionListenerPromise listenerPromise = new HTTPSessionListenerPromise(context);
context.put(HTTP2ClientConnectionFactory.CLIENT_CONTEXT_KEY, client); context.put(HTTP2ClientConnectionFactory.CLIENT_CONTEXT_KEY, client);
@ -62,5 +70,54 @@ public class ClientConnectionFactoryOverHTTP2 implements ClientConnectionFactory
{ {
super(List.of("h2c"), new ClientConnectionFactoryOverHTTP2(client)); super(List.of("h2c"), new ClientConnectionFactoryOverHTTP2(client));
} }
@Override
public void upgrade(EndPoint endPoint, Map<String, Object> context)
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise<HttpConnectionOverHTTP2>()
{
@Override
public void succeeded(HttpConnectionOverHTTP2 connection)
{
// This code is run when the client receives the server preface reply.
// Upgrade the connection to setup HTTP/2 frame listeners that will
// handle the HTTP/2 response to the upgrade request.
promise.succeeded(connection);
connection.upgrade(context);
// The connection can be used only after the upgrade that
// creates stream #1 corresponding to the HTTP/1.1 upgrade
// request, otherwise other requests can steal id #1.
destination.accept(connection);
}
@Override
public void failed(Throwable x)
{
promise.failed(x);
}
});
upgrade(destination.getClientConnectionFactory(), endPoint, context);
}
private void upgrade(ClientConnectionFactory factory, EndPoint endPoint, Map<String, Object> context)
{
try
{
// Avoid double TLS wrapping. We want to keep the existing
// SslConnection that has already performed the TLS handshake,
// and just upgrade the nested connection.
if (factory instanceof SslClientConnectionFactory && endPoint instanceof SslConnection.DecryptedEndPoint)
factory = ((SslClientConnectionFactory)factory).getClientConnectionFactory();
var newConnection = factory.newConnection(endPoint, context);
endPoint.upgrade(newConnection);
}
catch (IOException x)
{
throw new UncheckedIOException(x);
}
}
} }
} }

View File

@ -62,7 +62,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Promise<Connection> connectionPromise() private Promise<Connection> httpConnectionPromise()
{ {
return (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY); return (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
} }
@ -77,6 +77,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
if (destination instanceof HttpDestination.Multiplexed) if (destination instanceof HttpDestination.Multiplexed)
((HttpDestination.Multiplexed)destination).setMaxRequestsPerConnection(settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS)); ((HttpDestination.Multiplexed)destination).setMaxRequestsPerConnection(settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS));
} }
// The first SETTINGS frame is the server preface reply.
if (!connection.isMarked()) if (!connection.isMarked())
onServerPreface(session); onServerPreface(session);
} }
@ -85,7 +86,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
{ {
HttpConnectionOverHTTP2 connection = newHttpConnection(destination(), session); HttpConnectionOverHTTP2 connection = newHttpConnection(destination(), session);
if (this.connection.compareAndSet(null, connection, false, true)) if (this.connection.compareAndSet(null, connection, false, true))
connectionPromise().succeeded(connection); httpConnectionPromise().succeeded(connection);
} }
protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session) protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session)
@ -105,6 +106,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
void onClose(HttpConnectionOverHTTP2 connection, GoAwayFrame frame) void onClose(HttpConnectionOverHTTP2 connection, GoAwayFrame frame)
{ {
connection.close();
} }
@Override @Override
@ -133,7 +135,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
{ {
boolean result = connection.compareAndSet(null, null, false, true); boolean result = connection.compareAndSet(null, null, false, true);
if (result) if (result)
connectionPromise().failed(failure); httpConnectionPromise().failed(failure);
return result; return result;
} }
} }

View File

@ -19,6 +19,8 @@
package org.eclipse.jetty.http2.client.http; package org.eclipse.jetty.http2.client.http;
import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousCloseException;
import java.util.List;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -32,13 +34,19 @@ import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpRequest; import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponse;
import org.eclipse.jetty.client.HttpUpgrader; import org.eclipse.jetty.client.HttpUpgrader;
import org.eclipse.jetty.client.SendFailure; import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.CloseState;
import org.eclipse.jetty.http2.ErrorCode; import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.HTTP2Session;
import org.eclipse.jetty.http2.IStream; import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Session; import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.Sweeper; import org.eclipse.jetty.util.thread.Sweeper;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -90,6 +98,33 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
return send(channel, exchange); return send(channel, exchange);
} }
public void upgrade(Map<String, Object> context)
{
HttpResponse response = (HttpResponse)context.get(HttpResponse.class.getName());
HttpRequest request = (HttpRequest)response.getRequest();
// In case of HTTP/1.1 upgrade to HTTP/2, the request is HTTP/1.1
// (with upgrade) for a resource, and the response is HTTP/2.
// Create the implicit stream#1 so that it can receive the HTTP/2 response.
MetaData.Request metaData = new MetaData.Request(request.getMethod(), new HttpURI(request.getURI()), HttpVersion.HTTP_2, request.getHeaders());
// We do not support upgrade requests with content, so endStream=true.
HeadersFrame frame = new HeadersFrame(metaData, null, true);
IStream stream = ((HTTP2Session)session).newStream(frame, null);
stream.updateClose(frame.isEndStream(), CloseState.Event.AFTER_SEND);
HttpExchange exchange = request.getConversation().getExchanges().peekLast();
HttpChannelOverHTTP2 http2Channel = acquireHttpChannel();
activeChannels.add(http2Channel);
HttpExchange newExchange = new HttpExchange(exchange.getHttpDestination(), exchange.getRequest(), List.of());
http2Channel.associate(newExchange);
stream.setListener(http2Channel.getStreamListener());
http2Channel.setStream(stream);
newExchange.requestComplete(null);
newExchange.terminateRequest();
if (LOG.isDebugEnabled())
LOG.debug("Upgrade completed for {}", this);
}
@Override @Override
protected void normalizeRequest(Request request) protected void normalizeRequest(Request request)
{ {

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.http2.client.http;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -116,7 +115,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
conversation.setAttribute(EndPoint.class.getName(), endPoint); conversation.setAttribute(EndPoint.class.getName(), endPoint);
HttpUpgrader upgrader = (HttpUpgrader)conversation.getAttribute(HttpUpgrader.class.getName()); HttpUpgrader upgrader = (HttpUpgrader)conversation.getAttribute(HttpUpgrader.class.getName());
if (upgrader != null) if (upgrader != null)
upgrader.upgrade(httpResponse, endPoint); upgrade(upgrader, httpResponse, endPoint);
} }
if (responseHeaders(exchange)) if (responseHeaders(exchange))
@ -148,6 +147,18 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
} }
} }
private void upgrade(HttpUpgrader upgrader, HttpResponse response, EndPoint endPoint)
{
try
{
upgrader.upgrade(response, endPoint, Callback.from(Callback.NOOP::succeeded, this::responseFailure));
}
catch (Throwable x)
{
responseFailure(x);
}
}
Stream.Listener onPush(Stream stream, PushPromiseFrame frame) Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{ {
HttpExchange exchange = getHttpExchange(); HttpExchange exchange = getHttpExchange();
@ -166,8 +177,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
if (listener != null) if (listener != null)
{ {
HttpChannelOverHTTP2 pushChannel = getHttpChannel().getHttpConnection().acquireHttpChannel(); HttpChannelOverHTTP2 pushChannel = getHttpChannel().getHttpConnection().acquireHttpChannel();
List<Response.ResponseListener> listeners = Collections.singletonList(listener); HttpExchange pushExchange = new HttpExchange(getHttpDestination(), pushRequest, List.of(listener));
HttpExchange pushExchange = new HttpExchange(getHttpDestination(), pushRequest, listeners);
pushChannel.associate(pushExchange); pushChannel.associate(pushExchange);
pushChannel.setStream(stream); pushChannel.setStream(stream);
// TODO: idle timeout ? // TODO: idle timeout ?

View File

@ -38,12 +38,12 @@ import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log; import org.slf4j.Logger;
import org.eclipse.jetty.util.log.Logger; import org.slf4j.LoggerFactory;
public class HttpSenderOverHTTP2 extends HttpSender public class HttpSenderOverHTTP2 extends HttpSender
{ {
private static final Logger LOG = Log.getLogger(HttpSenderOverHTTP2.class); private static final Logger LOG = LoggerFactory.getLogger(HttpSenderOverHTTP2.class);
public HttpSenderOverHTTP2(HttpChannelOverHTTP2 channel) public HttpSenderOverHTTP2(HttpChannelOverHTTP2 channel)
{ {

View File

@ -80,7 +80,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -74,13 +74,13 @@ public class HTTP2CServerConnectionFactory extends HTTP2ServerConnectionFactory
public Connection upgradeConnection(Connector connector, EndPoint endPoint, Request request, HttpFields response101) throws BadMessageException public Connection upgradeConnection(Connector connector, EndPoint endPoint, Request request, HttpFields response101) throws BadMessageException
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} upgraded {}{}", this, request.toString(), request.getFields()); LOG.debug("{} upgrading {}{}{}", this, request, System.lineSeparator(), request.getFields());
if (request.getContentLength() > 0) if (request.getContentLength() > 0)
return null; return null;
HTTP2ServerConnection connection = (HTTP2ServerConnection)newConnection(connector, endPoint); HTTP2ServerConnection connection = (HTTP2ServerConnection)newConnection(connector, endPoint);
if (connection.upgrade(request)) if (connection.upgrade(request, response101))
return connection; return connection;
return null; return null;
} }

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.http2.server;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
@ -33,6 +32,7 @@ import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
@ -54,7 +54,6 @@ import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.ServerParser;
import org.eclipse.jetty.http2.parser.SettingsBodyParser; import org.eclipse.jetty.http2.parser.SettingsBodyParser;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
@ -63,7 +62,7 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.CountingCallback; import org.eclipse.jetty.util.CountingCallback;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
public class HTTP2ServerConnection extends HTTP2Connection implements Connection.UpgradeTo public class HTTP2ServerConnection extends HTTP2Connection
{ {
/** /**
* @param protocol An HTTP2 protocol variant * @param protocol An HTTP2 protocol variant
@ -132,21 +131,14 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
this.recycleHttpChannels = recycleHttpChannels; this.recycleHttpChannels = recycleHttpChannels;
} }
@Override
public void onUpgradeTo(ByteBuffer buffer)
{
if (LOG.isDebugEnabled())
LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
setInputBuffer(buffer);
}
@Override @Override
public void onOpen() public void onOpen()
{ {
notifyAccept(getSession()); ISession session = getSession();
notifyAccept(session);
for (Frame frame : upgradeFrames) for (Frame frame : upgradeFrames)
{ {
getSession().onFrame(frame); session.onFrame(frame);
} }
super.onOpen(); super.onOpen();
produce(); produce();
@ -328,7 +320,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
} }
} }
public boolean upgrade(Request request) public boolean upgrade(Request request, HttpFields responseFields)
{ {
if (HttpMethod.PRI.is(request.getMethod())) if (HttpMethod.PRI.is(request.getMethod()))
{ {
@ -343,7 +335,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
final byte[] settings = Base64.getUrlDecoder().decode(value == null ? "" : value); final byte[] settings = Base64.getUrlDecoder().decode(value == null ? "" : value);
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} settings {}", this, TypeUtil.toHexString(settings)); LOG.debug("{} {}: {}", this, HttpHeader.HTTP2_SETTINGS, TypeUtil.toHexString(settings));
SettingsFrame settingsFrame = SettingsBodyParser.parseBody(BufferUtil.toBuffer(settings)); SettingsFrame settingsFrame = SettingsBodyParser.parseBody(BufferUtil.toBuffer(settings));
if (settingsFrame == null) if (settingsFrame == null)
@ -352,11 +344,18 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
throw new BadMessageException(); throw new BadMessageException();
} }
responseFields.put(HttpHeader.UPGRADE, "h2c");
responseFields.put(HttpHeader.CONNECTION, "Upgrade");
getParser().standardUpgrade(); getParser().standardUpgrade();
// We fake that we received a client preface, so that we can send the
// server preface as the first HTTP/2 frame as required by the spec.
// When the client sends the real preface, the parser won't notify it.
upgradeFrames.add(new PrefaceFrame()); upgradeFrames.add(new PrefaceFrame());
// This is the settings from the HTTP2-Settings header.
upgradeFrames.add(settingsFrame); upgradeFrames.add(settingsFrame);
// Remember the request to send a response from onOpen(). // Remember the request to send a response.
upgradeFrames.add(new HeadersFrame(1, new Request(request), null, true)); upgradeFrames.add(new HeadersFrame(1, new Request(request), null, true));
} }
return true; return true;

View File

@ -53,6 +53,15 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.infinispan</groupId> <groupId>org.infinispan</groupId>
<artifactId>infinispan-client-hotrod</artifactId> <artifactId>infinispan-client-hotrod</artifactId>

View File

@ -0,0 +1,145 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// 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.session.infinispan;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
/**
* BoundDelegatingInputStream
*
* An InputStream that delegates methods to an ObjectInput. The ObjectInput must start
* with an integer containing the length of the data.
*/
public class BoundDelegatingInputStream extends InputStream
{
protected final ObjectInput objectInput;
private final int length;
private int position = 0;
public BoundDelegatingInputStream(ObjectInput objectInput) throws IOException
{
this.objectInput = objectInput;
this.length = objectInput.readInt();
}
@Override
public int read() throws IOException
{
if (position < length)
{
position++;
return objectInput.read();
}
return -1;
}
@Override
public int read(byte[] b) throws IOException
{
int available = length - position;
int read = -1;
if (position == length)
{
return read;
}
if (b.length > available)
{
read = objectInput.read(b, 0, available);
}
else
{
read = objectInput.read(b);
}
if (read != -1)
{
position += read;
}
return read;
}
@Override
public int read(byte[] b, int off, int len) throws IOException
{
int read = -1;
if (position == length)
{
return read;
}
if (position + len > length)
{
read = objectInput.read(b, off, length - position);
}
else
{
read = objectInput.read(b, off, len);
}
if (read != -1)
{
position += read;
}
return read;
}
@Override
public long skip(long n) throws IOException
{
long skip = 0;
if (position + n < length)
{
skip = objectInput.skip(length - position);
}
else
{
skip = objectInput.skip(n);
}
if (skip > 0)
{
position += skip;
}
return skip;
}
@Override
public int available() throws IOException
{
if (position < length)
{
int available = objectInput.available();
if (position + available > length)
{
return length - position;
}
else
{
return available;
}
}
return 0;
}
@Override
public void close() throws IOException
{
objectInput.close();
}
}

View File

@ -26,6 +26,7 @@ import java.util.Map;
import org.eclipse.jetty.server.session.SessionData; import org.eclipse.jetty.server.session.SessionData;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream; import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.infinispan.commons.marshall.SerializeWith;
/** /**
* InfinispanSessionData * InfinispanSessionData
@ -37,6 +38,7 @@ import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
* pool and thus these threads have no knowledge of the correct classloader to * pool and thus these threads have no knowledge of the correct classloader to
* use. * use.
*/ */
@SerializeWith(SessionDataMarshaller.class)
public class InfinispanSessionData extends SessionData public class InfinispanSessionData extends SessionData
{ {
protected byte[] _serializedAttributes; protected byte[] _serializedAttributes;

View File

@ -19,8 +19,14 @@
package org.eclipse.jetty.session.infinispan; package org.eclipse.jetty.session.infinispan;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.infinispan.commons.marshall.Externalizer;
import org.infinispan.protostream.FileDescriptorSource;
import org.infinispan.protostream.MessageMarshaller; import org.infinispan.protostream.MessageMarshaller;
import org.infinispan.protostream.ProtobufUtil;
import org.infinispan.protostream.SerializationContext;
/** /**
* SessionDataMarshaller * SessionDataMarshaller
@ -30,13 +36,30 @@ import org.infinispan.protostream.MessageMarshaller;
* control to ensure that session attributes can be deserialized using either * control to ensure that session attributes can be deserialized using either
* the container class loader or the webapp classloader, as appropriate. * the container class loader or the webapp classloader, as appropriate.
*/ */
public class SessionDataMarshaller implements MessageMarshaller<InfinispanSessionData> public class SessionDataMarshaller
implements MessageMarshaller<InfinispanSessionData>, Externalizer<InfinispanSessionData>
{ {
/** /**
* The version of the serializer. * The version of the serializer.
*/ */
private static final int VERSION = 0; private static final int VERSION = 0;
private static SerializationContext serializationContext;
private static synchronized void initSerializationContext() throws IOException
{
if (serializationContext != null)
{
return;
}
FileDescriptorSource fds = new FileDescriptorSource();
fds.addProtoFiles("/session.proto");
SerializationContext sCtx = ProtobufUtil.newSerializationContext();
sCtx.registerProtoFiles(fds);
sCtx.registerMarshaller(new SessionDataMarshaller());
serializationContext = sCtx;
}
@Override @Override
public Class<? extends InfinispanSessionData> getJavaClass() public Class<? extends InfinispanSessionData> getJavaClass()
{ {
@ -49,6 +72,39 @@ public class SessionDataMarshaller implements MessageMarshaller<InfinispanSessio
return "org_eclipse_jetty_session_infinispan.InfinispanSessionData"; return "org_eclipse_jetty_session_infinispan.InfinispanSessionData";
} }
@Override
public InfinispanSessionData readObject(ObjectInput input) throws IOException, ClassNotFoundException
{
if (serializationContext == null)
{
initSerializationContext();
}
// invokes readFrom(ProtoStreamReader)
InfinispanSessionData data = ProtobufUtil.readFrom(serializationContext, new BoundDelegatingInputStream(input),
InfinispanSessionData.class);
if (data != null)
{
data.deserializeAttributes();
}
return data;
}
@Override
public void writeObject(ObjectOutput output, InfinispanSessionData object) throws IOException
{
if (serializationContext == null)
{
initSerializationContext();
}
// invokes writeTo(ProtoStreamWriter, InfinispanSessionData)
byte[] data = ProtobufUtil.toByteArray(serializationContext, object);
int length = data.length;
output.writeInt(length);
output.write(data);
}
@Override @Override
public InfinispanSessionData readFrom(ProtoStreamReader in) throws IOException public InfinispanSessionData readFrom(ProtoStreamReader in) throws IOException
{ {
@ -67,7 +123,8 @@ public class SessionDataMarshaller implements MessageMarshaller<InfinispanSessio
final long expiry = in.readLong("expiry"); final long expiry = in.readLong("expiry");
final long maxInactiveMs = in.readLong("maxInactiveMs"); final long maxInactiveMs = in.readLong("maxInactiveMs");
InfinispanSessionData sd = new InfinispanSessionData(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs); InfinispanSessionData sd = new InfinispanSessionData(id, cpath, vhost, created, accessed, lastAccessed,
maxInactiveMs);
sd.setCookieSet(cookieSet); sd.setCookieSet(cookieSet);
sd.setLastNode(lastNode); sd.setLastNode(lastNode);
sd.setExpiry(expiry); sd.setExpiry(expiry);
@ -103,4 +160,5 @@ public class SessionDataMarshaller implements MessageMarshaller<InfinispanSessio
sdata.serializeAttributes(); sdata.serializeAttributes();
out.writeBytes("attributes", sdata.getSerializedAttributes()); out.writeBytes("attributes", sdata.getSerializedAttributes());
} }
} }

View File

@ -25,7 +25,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -70,7 +70,7 @@ public interface ClientConnectionFactory
* (for example {@code ["h2", "h2-17", "h2-16"]}) and a {@link ClientConnectionFactory} * (for example {@code ["h2", "h2-17", "h2-16"]}) and a {@link ClientConnectionFactory}
* that creates connections that speak that network protocol.</p> * that creates connections that speak that network protocol.</p>
*/ */
public static class Info public static class Info extends ContainerLifeCycle
{ {
private final List<String> protocols; private final List<String> protocols;
private final ClientConnectionFactory factory; private final ClientConnectionFactory factory;
@ -79,6 +79,7 @@ public interface ClientConnectionFactory
{ {
this.protocols = protocols; this.protocols = protocols;
this.factory = factory; this.factory = factory;
addBean(factory);
} }
public List<String> getProtocols() public List<String> getProtocols()
@ -102,6 +103,11 @@ public interface ClientConnectionFactory
return protocols.stream().anyMatch(p -> candidates.stream().anyMatch(c -> c.equalsIgnoreCase(p))); return protocols.stream().anyMatch(p -> candidates.stream().anyMatch(c -> c.equalsIgnoreCase(p)));
} }
public void upgrade(EndPoint endPoint, Map<String, Object> context)
{
throw new UnsupportedOperationException(this + " does not support upgrade to another protocol");
}
@Override @Override
public String toString() public String toString()
{ {

View File

@ -56,6 +56,11 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
this.connectionFactory = connectionFactory; this.connectionFactory = connectionFactory;
} }
public ClientConnectionFactory getClientConnectionFactory()
{
return connectionFactory;
}
public void setDirectBuffersForEncryption(boolean useDirectBuffers) public void setDirectBuffersForEncryption(boolean useDirectBuffers)
{ {
this._directBuffersForEncryption = useDirectBuffers; this._directBuffersForEncryption = useDirectBuffers;

View File

@ -42,13 +42,17 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.slf4j</groupId>
<artifactId>jetty-test-helper</artifactId> <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>jetty-test-helper</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -0,0 +1,3 @@
# Jetty Logging using jetty-slf4j-impl
org.eclipse.jetty.LEVEL=INFO
org.apache.directory.LEVEL=ERROR

View File

@ -1 +0,0 @@
org.slf4j.simpleLogger.log.org.apache.directory=error

View File

@ -50,6 +50,15 @@
<artifactId>jetty-security</artifactId> <artifactId>jetty-security</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId> <artifactId>jetty-test-helper</artifactId>

View File

@ -32,24 +32,25 @@
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version> <scope>test</scope>
<scope>provided</scope> </dependency>
<optional>true</optional> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.openpojo</groupId> <groupId>com.openpojo</groupId>
<artifactId>openpojo</artifactId> <artifactId>openpojo</artifactId>

View File

@ -51,7 +51,15 @@
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId> <artifactId>jetty-test-helper</artifactId>

View File

@ -93,6 +93,18 @@
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.4.0-b180830.0359</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>javax.activation-api</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</plugin> </plugin>
<plugin> <plugin>
@ -121,40 +133,7 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>jdk9+</id>
<activation>
<jdk>[1.9,)</jdk>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.4.0-b180830.0359</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>javax.activation-api</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project> </project>

View File

@ -21,11 +21,6 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId> <artifactId>jetty-jmx</artifactId>
@ -39,6 +34,20 @@
<type>jar</type> <type>jar</type>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.tests</groupId> <groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-common</artifactId> <artifactId>test-sessions-common</artifactId>

View File

@ -66,6 +66,15 @@
<artifactId>jetty-util-ajax</artifactId> <artifactId>jetty-util-ajax</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId> <artifactId>jetty-servlet</artifactId>

View File

@ -39,7 +39,18 @@
<directory>src/main/context</directory> <directory>src/main/context</directory>
</resource> </resource>
</resources> </resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<!-- No point building javadoc on testing projects -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@ -19,7 +19,18 @@
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
</resource> </resource>
</resources> </resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<!-- No point building javadoc on testing projects -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@ -31,6 +31,18 @@
</dependencies> </dependencies>
<build> <build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<!-- No point building javadoc on testing projects -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@ -36,7 +36,18 @@
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
</resource> </resource>
</resources> </resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<!-- No point building javadoc on testing projects -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@ -477,6 +477,14 @@
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<!-- No point building javadoc on testing projects -->
<skip>true</skip>
</configuration>
</plugin>
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>

View File

@ -53,11 +53,6 @@
<artifactId>derby</artifactId> <artifactId>derby</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jndi</artifactId> <artifactId>jetty-jndi</artifactId>
@ -68,11 +63,13 @@
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.derby</groupId> <groupId>org.slf4j</groupId>
<artifactId>derby</artifactId> <artifactId>slf4j-api</artifactId>
<version>10.1.2.1</version> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -57,11 +57,6 @@
<artifactId>jetty-alpn-client</artifactId> <artifactId>jetty-alpn-client</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util-ajax</artifactId> <artifactId>jetty-util-ajax</artifactId>
@ -77,7 +72,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -30,7 +30,15 @@
<artifactId>jetty-annotations</artifactId> <artifactId>jetty-annotations</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId> <artifactId>jetty-test-helper</artifactId>

View File

@ -36,7 +36,15 @@
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-servlet-api</artifactId> <artifactId>jetty-servlet-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.tests</groupId> <groupId>org.eclipse.jetty.tests</groupId>
<artifactId>jetty-http-tools</artifactId> <artifactId>jetty-http-tools</artifactId>

View File

@ -131,5 +131,14 @@
<artifactId>apache-jstl</artifactId> <artifactId>apache-jstl</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -0,0 +1,2 @@
# Jetty Logging using jetty-slf4j-impl
org.eclipse.jetty.LEVEL=INFO

View File

@ -58,7 +58,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -89,6 +89,11 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -441,18 +441,12 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
throw new BadMessageException(HttpStatus.BAD_REQUEST_400); throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
// Find the upgrade factory // Find the upgrade factory
ConnectionFactory.Upgrading factory = null; ConnectionFactory.Upgrading factory = getConnector().getConnectionFactories().stream()
for (ConnectionFactory f : getConnector().getConnectionFactories()) .filter(f -> f instanceof ConnectionFactory.Upgrading)
{ .map(ConnectionFactory.Upgrading.class::cast)
if (f instanceof ConnectionFactory.Upgrading) .filter(f -> f.getProtocols().contains(_upgrade.getValue()))
{ .findAny()
if (f.getProtocols().contains(_upgrade.getValue())) .orElse(null);
{
factory = (ConnectionFactory.Upgrading)f;
break;
}
}
}
if (factory == null) if (factory == null)
{ {

View File

@ -693,8 +693,16 @@ public class DetectorConnectionTest
start(detector, http); start(detector, http);
String request = "AAAA".repeat(32768); String request = "AAAA".repeat(32768);
String response = getResponse(request);
assertThat(response, Matchers.nullValue()); try
{
String response = getResponse(request);
assertThat(response, Matchers.nullValue());
}
catch (SocketException expected)
{
// The test may fail writing the "request"
// bytes as the server sends back a TCP RST.
}
} }
} }

View File

@ -60,7 +60,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -71,7 +71,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -57,11 +57,11 @@ public class StdErrAppender implements JettyAppender
/** /**
* The stream to write logging events to. * The stream to write logging events to.
*/ */
private PrintStream stderr; private PrintStream stream;
public StdErrAppender(JettyLoggerConfiguration config) public StdErrAppender(JettyLoggerConfiguration config)
{ {
this(config, System.err); this(config, null);
} }
public StdErrAppender(JettyLoggerConfiguration config, PrintStream stream) public StdErrAppender(JettyLoggerConfiguration config, PrintStream stream)
@ -72,7 +72,7 @@ public class StdErrAppender implements JettyAppender
public StdErrAppender(JettyLoggerConfiguration config, PrintStream stream, TimeZone timeZone) public StdErrAppender(JettyLoggerConfiguration config, PrintStream stream, TimeZone timeZone)
{ {
Objects.requireNonNull(config, "JettyLoggerConfiguration"); Objects.requireNonNull(config, "JettyLoggerConfiguration");
this.stderr = Objects.requireNonNull(stream, "PrintStream"); this.stream = stream;
TimeZone tzone = timeZone; TimeZone tzone = timeZone;
if (tzone == null) if (tzone == null)
@ -96,7 +96,14 @@ public class StdErrAppender implements JettyAppender
{ {
StringBuilder builder = new StringBuilder(64); StringBuilder builder = new StringBuilder(64);
format(builder, logger, level, timestamp, threadName, throwable, message, argumentArray); format(builder, logger, level, timestamp, threadName, throwable, message, argumentArray);
stderr.println(builder); if (stream != null)
{
stream.println(builder);
}
else
{
System.err.println(builder);
}
} }
public boolean isCondensedNames() public boolean isCondensedNames()
@ -116,12 +123,12 @@ public class StdErrAppender implements JettyAppender
public PrintStream getStream() public PrintStream getStream()
{ {
return stderr; return stream;
} }
public void setStream(PrintStream stream) public void setStream(PrintStream stream)
{ {
this.stderr = stream; this.stream = stream;
} }
private void format(StringBuilder builder, JettyLogger logger, Level level, long timestamp, String threadName, Throwable throwable, String message, Object... argumentArray) private void format(StringBuilder builder, JettyLogger logger, Level level, long timestamp, String threadName, Throwable throwable, String message, Object... argumentArray)

View File

@ -65,50 +65,57 @@ public class Slf4jEffort
AtomicInteger countPomSlf4jImpls = new AtomicInteger(0); AtomicInteger countPomSlf4jImpls = new AtomicInteger(0);
AtomicInteger countOldLogClassProps = new AtomicInteger(0); AtomicInteger countOldLogClassProps = new AtomicInteger(0);
getProjectsStream(root).forEach((pom) -> getProjectsStream(root)
{ .filter(pom ->
Path project = pom.getParent();
try
{ {
Path testLoggingProps = project.resolve("src/test/resources/jetty-logging.properties"); String fullpath = pom.toString();
return !((fullpath.contains("/jetty-osgi") ||
boolean isMainSrcUsingLogging = getSources(project.resolve("src/main/java")).anyMatch(Slf4jEffort::isUsingLogging); fullpath.contains("/jetty-slf4j-impl/")));
boolean isTestSrcUsingLogging = getSources(project.resolve("src/test/java")).anyMatch(Slf4jEffort::isUsingLogging); })
.forEach((pom) ->
if (isMainSrcUsingLogging || isTestSrcUsingLogging) {
Path project = pom.getParent();
try
{ {
// Must include slf4j in module-info and pom Path testLoggingProps = project.resolve("src/test/resources/jetty-logging.properties");
Path moduleInfo = project.resolve("src/main/java/module-info.java");
if (Files.exists(moduleInfo) && isMainSrcUsingLogging && !isLoggingJpmsPresent(moduleInfo))
{
System.err.printf("[Missing: JPMS] %s%n", moduleInfo);
countJpms.incrementAndGet();
}
if (!isSlf4jDepPresent(pom)) boolean isMainSrcUsingLogging = getSources(project.resolve("src/main/java")).anyMatch(Slf4jEffort::isUsingLogging);
{ boolean isTestSrcUsingLogging = getSources(project.resolve("src/test/java")).anyMatch(Slf4jEffort::isUsingLogging);
// System.err.printf("[Missing: Dep: slf4j-api] %s%n", pom);
countPomSlf4jApis.incrementAndGet();
}
if (isTestSrcUsingLogging && !isSlf4jImplDepPresent(pom)) if (isMainSrcUsingLogging || isTestSrcUsingLogging)
{ {
// System.err.printf("[Missing: Dep: jetty-slf4j-impl] %s%n", pom); if (!isSlf4jImplDepPresent(pom))
countPomSlf4jImpls.incrementAndGet(); {
} System.err.printf("[Missing: Dep: jetty-slf4j-impl] %s%n", pom);
countPomSlf4jImpls.incrementAndGet();
}
if (Files.exists(testLoggingProps) && isOldLogClassPropPresent(testLoggingProps)) // Must include slf4j in module-info and pom
{ Path moduleInfo = project.resolve("src/main/java/module-info.java");
System.err.printf("[Deprecated: log.class=LogImpl] %s%n", testLoggingProps); if (Files.exists(moduleInfo) && isMainSrcUsingLogging && !isLoggingJpmsPresent(moduleInfo))
countOldLogClassProps.incrementAndGet(); {
System.err.printf("[Missing: JPMS] %s%n", moduleInfo);
countJpms.incrementAndGet();
}
if (!isSlf4jDepPresent(pom))
{
System.err.printf("[Missing: Dep: slf4j-api] %s%n", pom);
countPomSlf4jApis.incrementAndGet();
}
if (Files.exists(testLoggingProps) && isOldLogClassPropPresent(testLoggingProps))
{
System.err.printf("[Deprecated: log.class=LogImpl] %s%n", testLoggingProps);
countOldLogClassProps.incrementAndGet();
}
} }
} }
} catch (IOException e)
catch (IOException e) {
{ e.printStackTrace();
e.printStackTrace(); }
} });
});
System.out.printf("JPMS (module-info.java) to fix: %d%n", countJpms.get()); System.out.printf("JPMS (module-info.java) to fix: %d%n", countJpms.get());
System.out.printf("POMS (pom.xml) - slf4j-api to fix: %d%n", countPomSlf4jApis.get()); System.out.printf("POMS (pom.xml) - slf4j-api to fix: %d%n", countPomSlf4jApis.get());
@ -184,7 +191,9 @@ public class Slf4jEffort
String line; String line;
while ((line = reader.readLine()) != null) while ((line = reader.readLine()) != null)
{ {
if (line.contains("requires org.slf4j;")) if (line.contains("requires org.slf4j;") ||
line.contains("requires transitive org.slf4j;") ||
line.contains("requires static org.slf4j;"))
{ {
return true; return true;
} }

View File

@ -70,7 +70,6 @@
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId> <artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -77,10 +77,6 @@
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
</exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -92,8 +92,8 @@ public class Modules implements Iterable<Module>
_modules.stream() _modules.stream()
.filter(m -> .filter(m ->
{ {
boolean included = all || m.getTags().stream().anyMatch(t -> include.contains(t)); boolean included = all || m.getTags().stream().anyMatch(include::contains);
boolean excluded = m.getTags().stream().anyMatch(t -> exclude.contains(t)); boolean excluded = m.getTags().stream().anyMatch(exclude::contains);
return included && !excluded; return included && !excluded;
}) })
.sorted() .sorted()
@ -274,7 +274,7 @@ public class Modules implements Iterable<Module>
public List<Module> getEnabled() public List<Module> getEnabled()
{ {
List<Module> enabled = _modules.stream().filter(m -> m.isEnabled()).collect(Collectors.toList()); List<Module> enabled = _modules.stream().filter(Module::isEnabled).collect(Collectors.toList());
TopologicalSort<Module> sort = new TopologicalSort<>(); TopologicalSort<Module> sort = new TopologicalSort<>();
for (Module module : enabled) for (Module module : enabled)
@ -303,7 +303,7 @@ public class Modules implements Iterable<Module>
public List<Module> getSortedAll() public List<Module> getSortedAll()
{ {
List<Module> all = new ArrayList(_modules); List<Module> all = new ArrayList<>(_modules);
TopologicalSort<Module> sort = new TopologicalSort<>(); TopologicalSort<Module> sort = new TopologicalSort<>();
for (Module module : all) for (Module module : all)
@ -569,17 +569,19 @@ public class Modules implements Iterable<Module>
_modules.stream().filter(Module::isEnabled).forEach(m -> _modules.stream().filter(Module::isEnabled).forEach(m ->
{ {
// Check dependencies // Check dependencies
m.getDepends().forEach(d -> m.getDepends().stream()
{ .filter(Module::isRequiredDependency)
Set<Module> providers = getAvailableProviders(d); .forEach(d ->
if (providers.stream().filter(Module::isEnabled).count() == 0)
{ {
if (unsatisfied.length() > 0) Set<Module> providers = getAvailableProviders(d);
unsatisfied.append(','); if (providers.stream().noneMatch(Module::isEnabled))
unsatisfied.append(m.getName()); {
StartLog.error("Module [%s] requires a module providing [%s] from one of %s%n", m.getName(), d, providers); if (unsatisfied.length() > 0)
} unsatisfied.append(',');
}); unsatisfied.append(m.getName());
StartLog.error("Module [%s] requires a module providing [%s] from one of %s%n", m.getName(), d, providers);
}
});
}); });
if (unsatisfied.length() > 0) if (unsatisfied.length() > 0)

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