Merge branch 'jetty-10.0.x' into jetty-11.0.x

This commit is contained in:
olivier lamy 2020-03-21 15:05:42 +10:00
commit 27e431ce41
138 changed files with 1645 additions and 363 deletions

View File

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

View File

@ -16,6 +16,15 @@
</build>
<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>
<groupId>org.eclipse.jetty.example-async-rest</groupId>
<artifactId>example-async-rest-jar</artifactId>

View File

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

View File

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

View File

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

View File

@ -30,7 +30,15 @@
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</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.http2</groupId>
<artifactId>http2-server</artifactId>

View File

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

View File

@ -51,6 +51,15 @@
<artifactId>jetty-alpn-server</artifactId>
<version>${project.version}</version>
</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.http2</groupId>
<artifactId>http2-server</artifactId>

View File

@ -44,6 +44,15 @@
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</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>

View File

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

View File

@ -78,5 +78,14 @@
<artifactId>jetty-annotations</artifactId>
<version>${project.version}</version>
</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>
</project>

View File

@ -32,6 +32,15 @@
<version>${project.version}</version>
<scope>compile</scope>
</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>
<build>
<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 void proceed()

View File

@ -52,6 +52,14 @@ public interface ConnectionPool extends Closeable
*/
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()},
* back to this ConnectionPool.</p>

View File

@ -216,6 +216,7 @@ public class HttpClient extends ContainerLifeCycle
handlers.put(new RedirectProtocolHandler(this));
handlers.put(new WWWAuthenticationProtocolHandler(this));
handlers.put(new ProxyAuthenticationProtocolHandler(this));
handlers.put(new UpgradeProtocolHandler());
decoderFactories.add(new GZIPContentDecoder.Factory(byteBufferPool));
@ -523,7 +524,7 @@ public class HttpClient extends ContainerLifeCycle
return new Origin(scheme, host, port, request.getTag(), protocol);
}
HttpDestination resolveDestination(Origin origin)
public HttpDestination resolveDestination(Origin origin)
{
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());
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
ProxyConfiguration proxyConfig = client.getProxyConfiguration();
proxy = proxyConfig.match(origin);
ClientConnectionFactory connectionFactory = client.getTransport();
@ -98,11 +103,11 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (tag instanceof ClientConnectionFactory.Decorator)
connectionFactory = ((ClientConnectionFactory.Decorator)tag).apply(connectionFactory);
this.connectionFactory = connectionFactory;
}
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
public void accept(Connection connection)
{
connectionPool.accept(connection);
}
@Override
@ -497,7 +502,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
{
return String.format("%s[%s]@%x%s,queue=%d,pool=%s",
HttpDestination.class.getSimpleName(),
asString(),
getOrigin(),
hashCode(),
proxy == null ? "" : "(via " + proxy + ")",
exchanges.size(),

View File

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

View File

@ -59,7 +59,7 @@ import org.slf4j.LoggerFactory;
*/
public abstract class HttpSender implements AsyncContentProvider.Listener
{
protected static final Logger LOG = LoggerFactory.getLogger(HttpSender.class);
private static final Logger LOG = LoggerFactory.getLogger(HttpSender.class);
private final AtomicReference<RequestState> requestState = new AtomicReference<>(RequestState.QUEUED);
private final AtomicReference<SenderState> senderState = new AtomicReference<>(SenderState.IDLE);
@ -363,11 +363,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
catch (RejectedExecutionException x)
{
if (LOG.isDebugEnabled())
{
if (x != failure)
x.addSuppressed(failure);
LOG.debug("Exchange aborted {}", exchange, x);
}
abort(exchange, failure);
}
}

View File

@ -18,15 +18,48 @@
package org.eclipse.jetty.client;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpVersion;
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
{
/**
* <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 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 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
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()
.map(p -> p.toLowerCase(Locale.ENGLISH))
.collect(Collectors.toList());
for (ClientConnectionFactory.Info factoryInfo : factoryInfos)
{
addBean(factoryInfo);
}
Arrays.stream(factoryInfos).forEach(this::addBean);
setConnectionPoolFactory(destination ->
new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1));
}
@ -133,13 +130,22 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
else
{
// Preserve the order of protocols chosen by the application.
// We need to keep multiple protocols in case the protocol
// is negotiated: e.g. [http/1.1, h2] negotiates [h2], but
// 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());
if (ssl)
{
// There may be protocol negotiation, so preserve the order
// of protocols chosen by the application.
// We need to keep multiple protocols in case the protocol
// is negotiated: e.g. [http/1.1, h2] negotiates [h2], but
// 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;
if (!protocols.isEmpty())
@ -179,6 +185,15 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
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
{
try

View File

@ -18,15 +18,10 @@
package org.eclipse.jetty.client.http;
import java.util.Locale;
import java.util.concurrent.atomic.LongAdder;
import org.eclipse.jetty.client.HttpChannel;
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.Result;
import org.eclipse.jetty.http.HttpFields;
@ -92,37 +87,6 @@ public class HttpChannelOverHTTP extends HttpChannel
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()
{
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.HttpConnection;
import org.eclipse.jetty.client.HttpConversation;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
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.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.EndPoint;
@ -277,17 +279,35 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements IConne
protected void normalizeRequest(Request request)
{
super.normalizeRequest(request);
if (request instanceof HttpProxy.TunnelRequest)
{
long connectTimeout = getHttpClient().getConnectTimeout();
request.timeout(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);
((HttpRequest)request).getConversation().setAttribute(HttpUpgrader.class.getName(), upgrader);
upgrader.prepare((HttpRequest)request);
if (request instanceof HttpUpgrader.Factory)
{
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

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

View File

@ -35,9 +35,13 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpSenderOverHTTP extends HttpSender
{
private static final Logger LOG = LoggerFactory.getLogger(HttpSenderOverHTTP.class);
private final HttpGenerator generator = new HttpGenerator();
private boolean shutdown;

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

@ -20,7 +20,7 @@ package org.eclipse.jetty.client.http;
import java.io.EOFException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -96,7 +96,7 @@ public class HttpReceiverOverHTTPTest
{
HttpRequest request = (HttpRequest)client.newRequest("http://localhost");
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);
assertTrue(associated);
exchange.requestComplete(null);

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,15 @@
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</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</groupId>
<artifactId>jetty-webapp</artifactId>

View File

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

View File

@ -35,6 +35,22 @@
<version>${project.version}</version>
<scope>provided</scope>
</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>
<groupId>javax.xml</groupId>
<artifactId>jaxws-api</artifactId>

View File

@ -16,11 +16,19 @@
// ========================================================================
//
package org.eclipse.jetty.client.http;
package org.eclipse.jetty.http.spi;
import org.eclipse.jetty.client.HttpResponse;
public interface HttpConnectionUpgrader
public final class LoggingUtil
{
public void upgrade(HttpResponse response, HttpConnectionOverHTTP connection);
/**
* 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
* execution (maven, CI, and IDE).
*/
public static void init()
{
// 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
{
static
{
LoggingUtil.init();
}
String host = "localhost";
HttpServer server;
int port;

View File

@ -41,6 +41,10 @@ import org.junit.jupiter.api.Test;
public class TestEndpointMultiplePublishProblem
{
static
{
LoggingUtil.init();
}
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
{
static
{
LoggingUtil.init();
}
/**
* 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>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>

View File

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

View File

@ -30,7 +30,6 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</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.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RetainableByteBuffer;
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.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);
@ -95,8 +96,11 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
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)
producer.setInputBuffer(buffer);
}

View File

@ -540,19 +540,10 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
boolean queued;
synchronized (this)
{
int streamId = frame.getStreamId();
if (streamId <= 0)
{
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());
HeadersFrame[] frameOut = new HeadersFrame[1];
stream = newStream(frame, frameOut);
stream.setListener(listener);
ControlEntry entry = new ControlEntry(frame, stream, new StreamPromiseCallback(promise, stream));
ControlEntry entry = new ControlEntry(frameOut[0], stream, new StreamPromiseCallback(promise, stream));
queued = flusher.append(entry);
}
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)
{
return new HTTP2Stream(scheduler, this, streamId, request, local);

View File

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

View File

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

View File

@ -19,16 +19,23 @@
package org.eclipse.jetty.http2.client.http;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List;
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.HTTP2ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.Connection;
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 HTTP2Client client;
@ -36,10 +43,11 @@ public class ClientConnectionFactoryOverHTTP2 implements ClientConnectionFactory
public ClientConnectionFactoryOverHTTP2(HTTP2Client client)
{
this.client = client;
addBean(client);
}
@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);
context.put(HTTP2ClientConnectionFactory.CLIENT_CONTEXT_KEY, client);
@ -62,5 +70,54 @@ public class ClientConnectionFactoryOverHTTP2 implements ClientConnectionFactory
{
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")
private Promise<Connection> connectionPromise()
private Promise<Connection> httpConnectionPromise()
{
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)
((HttpDestination.Multiplexed)destination).setMaxRequestsPerConnection(settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS));
}
// The first SETTINGS frame is the server preface reply.
if (!connection.isMarked())
onServerPreface(session);
}
@ -85,7 +86,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
{
HttpConnectionOverHTTP2 connection = newHttpConnection(destination(), session);
if (this.connection.compareAndSet(null, connection, false, true))
connectionPromise().succeeded(connection);
httpConnectionPromise().succeeded(connection);
}
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)
{
connection.close();
}
@Override
@ -133,7 +135,7 @@ class HTTPSessionListenerPromise extends Session.Listener.Adapter implements Pro
{
boolean result = connection.compareAndSet(null, null, false, true);
if (result)
connectionPromise().failed(failure);
httpConnectionPromise().failed(failure);
return result;
}
}

View File

@ -19,6 +19,8 @@
package org.eclipse.jetty.http2.client.http;
import java.nio.channels.AsynchronousCloseException;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
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.HttpExchange;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponse;
import org.eclipse.jetty.client.HttpUpgrader;
import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpURI;
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.HTTP2Session;
import org.eclipse.jetty.http2.IStream;
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.thread.Sweeper;
import org.slf4j.Logger;
@ -90,6 +98,33 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
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
protected void normalizeRequest(Request request)
{

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.http2.client.http;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.function.BiFunction;
@ -116,7 +115,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
conversation.setAttribute(EndPoint.class.getName(), endPoint);
HttpUpgrader upgrader = (HttpUpgrader)conversation.getAttribute(HttpUpgrader.class.getName());
if (upgrader != null)
upgrader.upgrade(httpResponse, endPoint);
upgrade(upgrader, httpResponse, endPoint);
}
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)
{
HttpExchange exchange = getHttpExchange();
@ -166,8 +177,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
if (listener != null)
{
HttpChannelOverHTTP2 pushChannel = getHttpChannel().getHttpConnection().acquireHttpChannel();
List<Response.ResponseListener> listeners = Collections.singletonList(listener);
HttpExchange pushExchange = new HttpExchange(getHttpDestination(), pushRequest, listeners);
HttpExchange pushExchange = new HttpExchange(getHttpDestination(), pushRequest, List.of(listener));
pushChannel.associate(pushExchange);
pushChannel.setStream(stream);
// TODO: idle timeout ?

View File

@ -37,9 +37,13 @@ import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpSenderOverHTTP2 extends HttpSender
{
private static final Logger LOG = LoggerFactory.getLogger(HttpSenderOverHTTP2.class);
public HttpSenderOverHTTP2(HttpChannelOverHTTP2 channel)
{
super(channel);

View File

@ -80,7 +80,6 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</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
{
if (LOG.isDebugEnabled())
LOG.debug("{} upgraded {}{}", this, request.toString(), request.getFields());
LOG.debug("{} upgrading {}{}{}", this, request, System.lineSeparator(), request.getFields());
if (request.getContentLength() > 0)
return null;
HTTP2ServerConnection connection = (HTTP2ServerConnection)newConnection(connector, endPoint);
if (connection.upgrade(request))
if (connection.upgrade(request, response101))
return connection;
return null;
}

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.http2.server;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
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.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
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.SettingsBodyParser;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
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.TypeUtil;
public class HTTP2ServerConnection extends HTTP2Connection implements Connection.UpgradeTo
public class HTTP2ServerConnection extends HTTP2Connection
{
/**
* @param protocol An HTTP2 protocol variant
@ -132,21 +131,14 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
this.recycleHttpChannels = recycleHttpChannels;
}
@Override
public void onUpgradeTo(ByteBuffer buffer)
{
if (LOG.isDebugEnabled())
LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
setInputBuffer(buffer);
}
@Override
public void onOpen()
{
notifyAccept(getSession());
ISession session = getSession();
notifyAccept(session);
for (Frame frame : upgradeFrames)
{
getSession().onFrame(frame);
session.onFrame(frame);
}
super.onOpen();
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()))
{
@ -343,7 +335,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
final byte[] settings = Base64.getUrlDecoder().decode(value == null ? "" : value);
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));
if (settingsFrame == null)
@ -352,11 +344,18 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection
throw new BadMessageException();
}
responseFields.put(HttpHeader.UPGRADE, "h2c");
responseFields.put(HttpHeader.CONNECTION, "Upgrade");
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());
// This is the settings from the HTTP2-Settings header.
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));
}
return true;

View File

@ -53,6 +53,15 @@
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</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.infinispan</groupId>
<artifactId>infinispan-client-hotrod</artifactId>

View File

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

View File

@ -70,7 +70,7 @@ public interface ClientConnectionFactory
* (for example {@code ["h2", "h2-17", "h2-16"]}) and a {@link ClientConnectionFactory}
* 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 ClientConnectionFactory factory;
@ -79,6 +79,7 @@ public interface ClientConnectionFactory
{
this.protocols = protocols;
this.factory = factory;
addBean(factory);
}
public List<String> getProtocols()
@ -102,6 +103,11 @@ public interface ClientConnectionFactory
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
public String toString()
{

View File

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

View File

@ -42,13 +42,17 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<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.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</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>
<version>${project.version}</version>
</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>

View File

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

View File

@ -51,7 +51,15 @@
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</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>

View File

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

View File

@ -66,6 +66,15 @@
<artifactId>jetty-util-ajax</artifactId>
<version>${project.version}</version>
</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</groupId>
<artifactId>jetty-servlet</artifactId>

View File

@ -68,7 +68,15 @@
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
</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.apache.derby</groupId>
<artifactId>derby</artifactId>

View File

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

View File

@ -30,7 +30,15 @@
<artifactId>jetty-annotations</artifactId>
<version>${project.version}</version>
</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>

View File

@ -36,7 +36,15 @@
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-servlet-api</artifactId>
</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.tests</groupId>
<artifactId>jetty-http-tools</artifactId>

View File

@ -131,5 +131,14 @@
<artifactId>apache-jstl</artifactId>
<version>${project.version}</version>
</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>
</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>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,15 @@
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</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>
</project>

View File

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

View File

@ -61,9 +61,6 @@
<systemPropertyVariables>
<mavenRepoPath>${settings.localRepository}</mavenRepoPath>
</systemPropertyVariables>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-modules org.slf4j
</argLine>
</configuration>
</plugin>
</plugins>
@ -86,6 +83,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.util;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.slf4j.LoggerFactory;
@ -63,6 +64,32 @@ public interface Promise<C>
}
}
/**
* <p>Creates a Promise from the given success and failure consumers.</p>
*
* @param success the consumer invoked when the promise is succeeded
* @param failure the consumer invoked when the promise is failed
* @param <T> the type of the result
* @return a new Promise wrapping the success and failure consumers.
*/
static <T> Promise<T> from(Consumer<T> success, Consumer<Throwable> failure)
{
return new Promise<>()
{
@Override
public void succeeded(T result)
{
success.accept(result);
}
@Override
public void failed(Throwable x)
{
failure.accept(x);
}
};
}
/**
* <p>Creates a promise from the given incomplete CompletableFuture.</p>
* <p>When the promise completes, either succeeding or failing, the

View File

@ -36,9 +36,9 @@ import java.util.Arrays;
*/
public class SearchPattern
{
static final int alphabetSize = 256;
private int[] table;
private byte[] pattern;
private static final int ALPHABET_SIZE = 256;
private final int[] table = new int[ALPHABET_SIZE];
private final byte[] pattern;
/**
* Produces a SearchPattern instance which can be used
@ -70,16 +70,11 @@ public class SearchPattern
private SearchPattern(byte[] pattern)
{
this.pattern = pattern;
if (pattern.length == 0)
throw new IllegalArgumentException("Empty Pattern");
//Build up the pre-processed table for this pattern.
table = new int[alphabetSize];
for (int i = 0; i < table.length; ++i)
{
table[i] = pattern.length;
}
Arrays.fill(table, pattern.length);
for (int i = 0; i < pattern.length - 1; ++i)
{
table[0xff & pattern[i]] = pattern.length - 1 - i;
@ -97,7 +92,7 @@ public class SearchPattern
*/
public int match(byte[] data, int offset, int length)
{
validate(data, offset, length);
validateArgs(data, offset, length);
int skip = offset;
while (skip <= offset + length - pattern.length)
@ -125,7 +120,7 @@ public class SearchPattern
*/
public int endsWith(byte[] data, int offset, int length)
{
validate(data, offset, length);
validateArgs(data, offset, length);
int skip = (pattern.length <= length) ? (offset + length - pattern.length) : offset;
while (skip < offset + length)
@ -136,10 +131,8 @@ public class SearchPattern
return (offset + length - skip);
}
if (skip + pattern.length - 1 < data.length)
skip += table[0xff & data[skip + pattern.length - 1]];
else
skip++;
// We can't use the pre-processed table as we are not matching on the full pattern.
skip++;
}
return 0;
@ -157,10 +150,9 @@ public class SearchPattern
*/
public int startsWith(byte[] data, int offset, int length, int matched)
{
validate(data, offset, length);
validateArgs(data, offset, length);
int matchedCount = 0;
for (int i = 0; i < pattern.length - matched && i < length; i++)
{
if (data[offset + i] == pattern[i + matched])
@ -180,7 +172,7 @@ public class SearchPattern
* @param offset The offset within the data to start the search
* @param length The length of the data to search
*/
private void validate(byte[] data, int offset, int length)
private void validateArgs(byte[] data, int offset, int length)
{
if (offset < 0)
throw new IllegalArgumentException("offset was negative");

View File

@ -32,10 +32,10 @@ public class SearchPatternTest
@Test
public void testBasicSearch()
{
byte[] p1 = new String("truth").getBytes(StandardCharsets.US_ASCII);
byte[] p2 = new String("evident").getBytes(StandardCharsets.US_ASCII);
byte[] p3 = new String("we").getBytes(StandardCharsets.US_ASCII);
byte[] d = new String("we hold these truths to be self evident").getBytes(StandardCharsets.US_ASCII);
byte[] p1 = "truth".getBytes(StandardCharsets.US_ASCII);
byte[] p2 = "evident".getBytes(StandardCharsets.US_ASCII);
byte[] p3 = "we".getBytes(StandardCharsets.US_ASCII);
byte[] d = "we hold these truths to be self evident".getBytes(StandardCharsets.US_ASCII);
// Testing Compiled Pattern p1 "truth"
SearchPattern sp1 = SearchPattern.compile(p1);
@ -65,8 +65,8 @@ public class SearchPatternTest
@Test
public void testDoubleMatch()
{
byte[] p = new String("violent").getBytes(StandardCharsets.US_ASCII);
byte[] d = new String("These violent delights have violent ends.").getBytes(StandardCharsets.US_ASCII);
byte[] p = "violent".getBytes(StandardCharsets.US_ASCII);
byte[] d = "These violent delights have violent ends.".getBytes(StandardCharsets.US_ASCII);
SearchPattern sp = SearchPattern.compile(p);
assertEquals(6, sp.match(d, 0, d.length));
assertEquals(-1, sp.match(d, 6, p.length - 1));
@ -113,8 +113,8 @@ public class SearchPatternTest
@Test
public void testAlmostMatch()
{
byte[] p = new String("violent").getBytes(StandardCharsets.US_ASCII);
byte[] d = new String("vio lent violen v iolent violin vioviolenlent viiolent").getBytes(StandardCharsets.US_ASCII);
byte[] p = "violent".getBytes(StandardCharsets.US_ASCII);
byte[] d = "vio lent violen v iolent violin vioviolenlent viiolent".getBytes(StandardCharsets.US_ASCII);
SearchPattern sp = SearchPattern.compile(p);
assertEquals(-1, sp.match(d, 0, d.length));
}
@ -123,14 +123,14 @@ public class SearchPatternTest
public void testOddSizedPatterns()
{
// Test Large Pattern
byte[] p = new String("pneumonoultramicroscopicsilicovolcanoconiosis").getBytes(StandardCharsets.US_ASCII);
byte[] d = new String("pneumon").getBytes(StandardCharsets.US_ASCII);
byte[] p = "pneumonoultramicroscopicsilicovolcanoconiosis".getBytes(StandardCharsets.US_ASCII);
byte[] d = "pneumon".getBytes(StandardCharsets.US_ASCII);
SearchPattern sp = SearchPattern.compile(p);
assertEquals(-1, sp.match(d, 0, d.length));
// Test Single Character Pattern
p = new String("s").getBytes(StandardCharsets.US_ASCII);
d = new String("the cake is a lie").getBytes(StandardCharsets.US_ASCII);
p = "s".getBytes(StandardCharsets.US_ASCII);
d = "the cake is a lie".getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(10, sp.match(d, 0, d.length));
}
@ -138,30 +138,36 @@ public class SearchPatternTest
@Test
public void testEndsWith()
{
byte[] p = new String("pneumonoultramicroscopicsilicovolcanoconiosis").getBytes(StandardCharsets.US_ASCII);
byte[] d = new String("pneumonoultrami").getBytes(StandardCharsets.US_ASCII);
byte[] p = "pneumonoultramicroscopicsilicovolcanoconiosis".getBytes(StandardCharsets.US_ASCII);
byte[] d = "pneumonoultrami".getBytes(StandardCharsets.US_ASCII);
SearchPattern sp = SearchPattern.compile(p);
assertEquals(15, sp.endsWith(d, 0, d.length));
p = new String("abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
d = new String("abcdefghijklmnopqrstuvwxyzabcdefghijklmno").getBytes(StandardCharsets.US_ASCII);
p = "abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
d = "abcdefghijklmnopqrstuvwxyzabcdefghijklmno".getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(0, sp.match(d, 0, d.length));
assertEquals(-1, sp.match(d, 1, d.length - 1));
assertEquals(15, sp.endsWith(d, 0, d.length));
p = new String("abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
d = new String("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
p = "abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
d = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(0, sp.match(d, 0, d.length));
assertEquals(26, sp.match(d, 1, d.length - 1));
assertEquals(26, sp.endsWith(d, 0, d.length));
//test no match
p = new String("hello world").getBytes(StandardCharsets.US_ASCII);
d = new String("there is definitely no match in here").getBytes(StandardCharsets.US_ASCII);
p = "hello world".getBytes(StandardCharsets.US_ASCII);
d = "there is definitely no match in here".getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(0, sp.endsWith(d, 0, d.length));
//Test with range on array.
p = "abcde".getBytes(StandardCharsets.US_ASCII);
d = "?abc00000".getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(3, sp.endsWith(d, 0, 4));
}
@Test
@ -178,39 +184,54 @@ public class SearchPatternTest
private void testStartsWith(String offset)
{
byte[] p = new String("abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
byte[] d = new String(offset + "ijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
byte[] p = "abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
byte[] d = (offset + "ijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
SearchPattern sp = SearchPattern.compile(p);
assertEquals(18 + offset.length(), sp.match(d, offset.length(), d.length - offset.length()));
assertEquals(-1, sp.match(d, offset.length() + 19, d.length - 19 - offset.length()));
assertEquals(26, sp.startsWith(d, offset.length(), d.length - offset.length(), 8));
p = new String("abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
d = new String(offset + "ijklmnopqrstuvwxyNOMATCH").getBytes(StandardCharsets.US_ASCII);
p = "abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
d = (offset + "ijklmnopqrstuvwxyNOMATCH").getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(0, sp.startsWith(d, offset.length(), d.length - offset.length(), 8));
p = new String("abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
d = new String(offset + "abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
p = "abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
d = (offset + "abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(26, sp.startsWith(d, offset.length(), d.length - offset.length(), 0));
//test no match
p = new String("hello world").getBytes(StandardCharsets.US_ASCII);
d = new String(offset + "there is definitely no match in here").getBytes(StandardCharsets.US_ASCII);
p = "hello world".getBytes(StandardCharsets.US_ASCII);
d = (offset + "there is definitely no match in here").getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(0, sp.startsWith(d, offset.length(), d.length - offset.length(), 0));
//test large pattern small buffer
p = new String("abcdefghijklmnopqrstuvwxyz").getBytes(StandardCharsets.US_ASCII);
d = new String(offset + "mnopqrs").getBytes(StandardCharsets.US_ASCII);
p = "abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.US_ASCII);
d = (offset + "mnopqrs").getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(19, sp.startsWith(d, offset.length(), d.length - offset.length(), 12));
//partial pattern
p = new String("abcdef").getBytes(StandardCharsets.US_ASCII);
d = new String(offset + "cde").getBytes(StandardCharsets.US_ASCII);
p = "abcdef".getBytes(StandardCharsets.US_ASCII);
d = (offset + "cde").getBytes(StandardCharsets.US_ASCII);
sp = SearchPattern.compile(p);
assertEquals(5, sp.startsWith(d, offset.length(), d.length - offset.length(), 2));
}
@Test
public void testExampleFrom4673()
{
SearchPattern pattern = SearchPattern.compile("\r\n------WebKitFormBoundaryhXfFAMfUnUKhmqT8".getBytes(StandardCharsets.US_ASCII));
byte[] data = new byte[]{118,97,108,117,101,49,
'\r','\n','-','-','-','-',
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0};
int length = 12;
int partialMatch = pattern.endsWith(data, 0, length);
System.err.println("match1: " + partialMatch);
}
}

View File

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

View File

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

View File

@ -31,6 +31,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.core.WebSocketConstants;
import org.eclipse.jetty.websocket.core.internal.WebSocketCore;
@ -71,20 +72,35 @@ public class HttpUpgraderOverHTTP implements HttpUpgrader
}
@Override
public void upgrade(HttpResponse response, EndPoint endPoint)
public void upgrade(HttpResponse response, EndPoint endPoint, Callback callback)
{
HttpRequest request = (HttpRequest)response.getRequest();
HttpFields requestHeaders = request.getHeaders();
if (!requestHeaders.get(HttpHeader.UPGRADE).equalsIgnoreCase("websocket"))
throw new HttpResponseException("Not a WebSocket Upgrade", response);
// Check the Accept hash
String reqKey = requestHeaders.get(HttpHeader.SEC_WEBSOCKET_KEY);
String expectedHash = WebSocketCore.hashKey(reqKey);
String respHash = response.getHeaders().get(HttpHeader.SEC_WEBSOCKET_ACCEPT);
if (!expectedHash.equalsIgnoreCase(respHash))
throw new HttpResponseException("Invalid Sec-WebSocket-Accept hash (was:" + respHash + ", expected:" + expectedHash + ")", response);
clientUpgradeRequest.upgrade(response, endPoint);
if (requestHeaders.contains(HttpHeader.UPGRADE, "websocket"))
{
HttpFields responseHeaders = response.getHeaders();
if (responseHeaders.contains(HttpHeader.CONNECTION, "upgrade"))
{
// Check the Accept hash
String reqKey = requestHeaders.get(HttpHeader.SEC_WEBSOCKET_KEY);
String expectedHash = WebSocketCore.hashKey(reqKey);
String respHash = responseHeaders.get(HttpHeader.SEC_WEBSOCKET_ACCEPT);
if (expectedHash.equalsIgnoreCase(respHash))
{
clientUpgradeRequest.upgrade(response, endPoint);
callback.succeeded();
}
else
callback.failed(new HttpResponseException("Invalid Sec-WebSocket-Accept hash (was: " + respHash + " expected: " + expectedHash + ")", response));
}
else
{
callback.failed(new HttpResponseException("WebSocket upgrade missing 'Connection: Upgrade' header", response));
}
}
else
{
callback.failed(new HttpResponseException("Not a WebSocket upgrade", response));
}
}
}

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.client.HttpUpgrader;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.core.WebSocketConstants;
public class HttpUpgraderOverHTTP2 implements HttpUpgrader
@ -47,8 +48,16 @@ public class HttpUpgraderOverHTTP2 implements HttpUpgrader
}
@Override
public void upgrade(HttpResponse response, EndPoint endPoint)
public void upgrade(HttpResponse response, EndPoint endPoint, Callback callback)
{
clientUpgradeRequest.upgrade(response, endPoint);
try
{
clientUpgradeRequest.upgrade(response, endPoint);
callback.succeeded();
}
catch (Throwable x)
{
callback.failed(x);
}
}
}

View File

@ -12,4 +12,4 @@
# org.eclipse.jetty.io.ManagedSelector.LEVEL=DEBUG
# org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.LEVEL=INFO
# org.eclipse.jetty.websocket.core.LEVEL=DEBUG
# org.eclipse.jetty.websocket.core.LEVEL=DEBUG

View File

@ -80,7 +80,15 @@
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-javax-websocket-api</artifactId>
</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>

View File

@ -34,6 +34,15 @@
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-javax-websocket-api</artifactId>
</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>

View File

@ -15,6 +15,10 @@
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-javax-client</artifactId>
@ -37,7 +41,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
@ -51,6 +55,14 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<!-- No point deploying testing projects -->
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>

View File

@ -30,7 +30,15 @@
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
</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>

View File

@ -71,7 +71,15 @@
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</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>

View File

@ -55,6 +55,15 @@
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-servlet-api</artifactId>
</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>

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