Merge remote-tracking branch 'origin/jetty-12.0.x' into fix/jetty-12-pathresource-alias-detection-in-jarfile
This commit is contained in:
commit
54cc1a3ff9
|
@ -9,6 +9,9 @@ on:
|
|||
schedule:
|
||||
- cron: '22 1 * * 2'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
|
|
|
@ -3,8 +3,14 @@ on:
|
|||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
permissions:
|
||||
issues: write # for actions/stale to close stale issues
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
|
|
|
@ -1,173 +1,79 @@
|
|||
#!groovy
|
||||
|
||||
/**
|
||||
* IMPORTANT: Changes here need to be reflected in 2 other files as well.
|
||||
* pom.xml
|
||||
* build/scripts/ci.sh
|
||||
*/
|
||||
|
||||
pipeline {
|
||||
agent {
|
||||
node { label 'linux' }
|
||||
}
|
||||
agent any
|
||||
// save some io during the build
|
||||
options {
|
||||
skipDefaultCheckout()
|
||||
durabilityHint('PERFORMANCE_OPTIMIZED')
|
||||
}
|
||||
stages {
|
||||
stage("Checkout Jetty") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
checkout scm
|
||||
stage("Parallel Stage") {
|
||||
parallel {
|
||||
stage("Build / Test - JDK17") {
|
||||
agent { node { label 'linux' } }
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout( time: 180, unit: 'MINUTES' ) {
|
||||
checkout scm
|
||||
mavenBuild( "jdk17", "clean install -Perrorprone", "maven3")
|
||||
// Collect up the jacoco execution results (only on main build)
|
||||
jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class',
|
||||
exclusionPattern: '' +
|
||||
// build tools
|
||||
'**/org/eclipse/jetty/ant/**' +
|
||||
',*/org/eclipse/jetty/maven/its/**' +
|
||||
',**/org/eclipse/jetty/its/**' +
|
||||
// example code / documentation
|
||||
',**/org/eclipse/jetty/embedded/**' +
|
||||
',**/org/eclipse/jetty/asyncrest/**' +
|
||||
',**/org/eclipse/jetty/demo/**' +
|
||||
// special environments / late integrations
|
||||
',**/org/eclipse/jetty/gcloud/**' +
|
||||
',**/org/eclipse/jetty/infinispan/**' +
|
||||
',**/org/eclipse/jetty/osgi/**' +
|
||||
',**/org/eclipse/jetty/http/spi/**' +
|
||||
// test classes
|
||||
',**/org/eclipse/jetty/tests/**' +
|
||||
',**/org/eclipse/jetty/test/**',
|
||||
execPattern: '**/target/jacoco.exec',
|
||||
classPattern: '**/target/classes',
|
||||
sourcePattern: '**/src/main/java'
|
||||
recordIssues id: "jdk17", name: "Static Analysis jdk17", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle(), errorProne(), spotBugs()]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Build & Test - JDK17") {
|
||||
stages {
|
||||
stage("Setup") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
echo "Install org.eclipse.jetty:build-resources"
|
||||
mavenBuild("jdk17", "clean install -f build", "maven3")
|
||||
echo "Install org.eclipse.jetty:jetty-project"
|
||||
mavenBuild("jdk17", "-N clean install", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /jetty-core/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
mavenBuild("jdk17", "clean install -f jetty-core", "maven3")
|
||||
mavenBuild("jdk17", "clean -f jetty-core", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /jetty-integrations/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
//cleanup all projects
|
||||
mavenBuild("jdk17", "clean install -f jetty-integrations", "maven3")
|
||||
mavenBuild("jdk17", "clean -f jetty-integrations", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /jetty-ee10/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
//cleanup all projects
|
||||
mavenBuild("jdk17", "clean install -f jetty-ee10", "maven3")
|
||||
mavenBuild("jdk17", "clean -f jetty-ee10", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /jetty-ee9/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
//cleanup all projects
|
||||
mavenBuild("jdk17", "clean install -f jetty-ee9", "maven3")
|
||||
mavenBuild("jdk17", "clean -f jetty-ee9", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /jetty-ee8/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
//cleanup all projects
|
||||
mavenBuild("jdk17", "clean install -f jetty-ee8", "maven3")
|
||||
mavenBuild("jdk17", "clean -f jetty-ee8", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /jetty-home/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
//cleanup all projects
|
||||
mavenBuild("jdk17", "clean install -pl :jetty-home", "maven3")
|
||||
mavenBuild("jdk17", "clean -pl :jetty-home", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Module : /tests/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
//cleanup all projects
|
||||
mavenBuild("jdk17", "clean install -f tests", "maven3")
|
||||
mavenBuild("jdk17", "clean -f tests", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
stage("Module : /jetty-p2/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
mavenBuild("jdk17", "clean install -f jetty-p2", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
stage("Module : /documentation/") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
mavenBuild("jdk17", "clean install -rf :documentation", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Javadocs") {
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout(time: 120, unit: 'MINUTES') {
|
||||
dir("${env.WORKSPACE}/buildy") {
|
||||
mavenBuild("jdk17", "clean install -DskipTests", "maven3")
|
||||
mavenBuild("jdk17", "javadoc:javadoc", "maven3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
failure {
|
||||
slackNotif()
|
||||
}
|
||||
unstable {
|
||||
slackNotif()
|
||||
}
|
||||
fixed {
|
||||
slackNotif()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def slackNotif() {
|
||||
script {
|
||||
try {
|
||||
if ( env.BRANCH_NAME == 'jetty-10.0.x' || env.BRANCH_NAME == 'jetty-11.0.x' || env.BRANCH_NAME == 'jetty-12.0.x' ) {
|
||||
//BUILD_USER = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
|
||||
// by ${BUILD_USER}
|
||||
COLOR_MAP = ['SUCCESS': 'good', 'FAILURE': 'danger', 'UNSTABLE': 'danger', 'ABORTED': 'danger']
|
||||
slackSend channel: '#jenkins',
|
||||
color: COLOR_MAP[currentBuild.currentResult],
|
||||
message: "*${currentBuild.currentResult}:* Job ${env.JOB_NAME} build ${env.BUILD_NUMBER} - ${env.BUILD_URL}"
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace()
|
||||
echo "skip failure slack notification: " + e.getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +101,7 @@ def mavenBuild(jdk, cmdline, mvnName) {
|
|||
}
|
||||
finally
|
||||
{
|
||||
junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml', allowEmptyResults: true, skipPublishingChecks: true
|
||||
junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml', allowEmptyResults: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
jetty-12.0.0-SNAPSHOT
|
||||
|
||||
jetty-12.0.0.alpha0 - 22 August 2022
|
||||
+ First alpha release of Jetty 12. A lot changes but very good changes!
|
||||
|
||||
jetty-11.0.11 - 21 June 2022
|
||||
+ 8184 All suffix globs except first fail to match if path has `.` character
|
||||
in prefix section
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketError;
|
|||
import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.ee10.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class WebSocketDocs
|
||||
|
@ -256,7 +257,7 @@ public class WebSocketDocs
|
|||
remote.sendBytes(bytes);
|
||||
|
||||
// Send a PING frame to the remote peer.
|
||||
remote.sendPing(ByteBuffer.allocate(8).putLong(System.nanoTime()).flip());
|
||||
remote.sendPing(ByteBuffer.allocate(8).putLong(NanoTime.now()).flip());
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
|
@ -425,7 +426,7 @@ public class WebSocketDocs
|
|||
public void onWebSocketConnect(Session session)
|
||||
{
|
||||
// Send to the remote peer the local nanoTime.
|
||||
ByteBuffer buffer = ByteBuffer.allocate(8).putLong(System.nanoTime()).flip();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(8).putLong(NanoTime.now()).flip();
|
||||
session.getRemote().sendPing(buffer, WriteCallback.NOOP);
|
||||
}
|
||||
|
||||
|
@ -436,7 +437,7 @@ public class WebSocketDocs
|
|||
long start = payload.getLong();
|
||||
|
||||
// Calculate the round-trip time.
|
||||
long roundTrip = System.nanoTime() - start;
|
||||
long roundTrip = NanoTime.since(start);
|
||||
}
|
||||
}
|
||||
// end::pingPongListener[]
|
||||
|
|
|
@ -120,14 +120,14 @@ public class HTTPServerDocs
|
|||
@Override
|
||||
public void onRequestBegin(Request request)
|
||||
{
|
||||
times.put(request, System.nanoTime());
|
||||
times.put(request, NanoTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Request request)
|
||||
{
|
||||
long begin = times.remove(request);
|
||||
long elapsed = System.nanoTime() - begin;
|
||||
long elapsed = NanoTime.since(begin);
|
||||
System.getLogger("timing").log(INFO, "Request {0} took {1} ns", request, elapsed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,27 +204,29 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<artifactId>jetty-infinispan-common</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote-query</artifactId>
|
||||
<artifactId>jetty-infinispan-remote</artifactId>
|
||||
<classifier>config</classifier>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-embedded</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<artifactId>jetty-infinispan-remote-query</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-embedded-query</artifactId>
|
||||
<artifactId>jetty-infinispan-embedded</artifactId>
|
||||
<classifier>config</classifier>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-infinispan-embedded-query</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -260,7 +262,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-keystore</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.memcached</groupId>
|
||||
|
@ -275,7 +277,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
<artifactId>jetty-osgi-alpn</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.osgi</groupId>
|
||||
|
@ -370,7 +372,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-slf4j-impl</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -184,27 +184,27 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<artifactId>jetty-infinispan-common</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote-query</artifactId>
|
||||
<artifactId>jetty-infinispan-remote</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-embedded</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<artifactId>jetty-infinispan-remote-query</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-embedded-query</artifactId>
|
||||
<artifactId>jetty-infinispan-embedded</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-infinispan-embedded-query</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -230,7 +230,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-keystore</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.memcached</groupId>
|
||||
|
@ -296,7 +296,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-slf4j-impl</artifactId>
|
||||
<version>10.0.8-SNAPSHOT</version>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.client.api.Connection;
|
|||
import org.eclipse.jetty.util.Attachable;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Pool;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
|
@ -277,7 +278,7 @@ public abstract class AbstractConnectionPool extends ContainerLifeCycle implemen
|
|||
{
|
||||
pending.decrementAndGet();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Not creating connection as pool is full, pending: {}", pending);
|
||||
LOG.debug("Not creating connection as pool {} is full, pending: {}", pool, pending);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -533,15 +534,17 @@ public abstract class AbstractConnectionPool extends ContainerLifeCycle implemen
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x[c=%d/%d/%d,a=%d,i=%d,q=%d]",
|
||||
return String.format("%s@%x[s=%s,c=%d/%d/%d,a=%d,i=%d,q=%d,p=%s]",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
getState(),
|
||||
getPendingConnectionCount(),
|
||||
getConnectionCount(),
|
||||
getMaxConnectionCount(),
|
||||
getActiveConnectionCount(),
|
||||
getIdleConnectionCount(),
|
||||
destination.getQueuedRequestCount());
|
||||
destination.getQueuedRequestCount(),
|
||||
pool);
|
||||
}
|
||||
|
||||
private class FutureConnection extends Promise.Completable<Connection>
|
||||
|
@ -591,7 +594,7 @@ public abstract class AbstractConnectionPool extends ContainerLifeCycle implemen
|
|||
private static class EntryHolder
|
||||
{
|
||||
private final Pool<Connection>.Entry entry;
|
||||
private final long creationNanos = System.nanoTime();
|
||||
private final long creationNanoTime = NanoTime.now();
|
||||
private final AtomicInteger usage = new AtomicInteger();
|
||||
|
||||
private EntryHolder(Pool<Connection>.Entry entry)
|
||||
|
@ -601,7 +604,7 @@ public abstract class AbstractConnectionPool extends ContainerLifeCycle implemen
|
|||
|
||||
private boolean isExpired(long timeoutNanos)
|
||||
{
|
||||
return System.nanoTime() - creationNanos >= timeoutNanos;
|
||||
return NanoTime.since(creationNanoTime) >= timeoutNanos;
|
||||
}
|
||||
|
||||
private boolean use(int maxUsage)
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.http.HttpField;
|
|||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.QuotedCSV;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -215,10 +216,10 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
|
||||
// Adjust the timeout of the new request, taking into account the
|
||||
// timeout of the previous request and the time already elapsed.
|
||||
long timeoutAt = request.getTimeoutAt();
|
||||
if (timeoutAt < Long.MAX_VALUE)
|
||||
long timeoutNanoTime = request.getTimeoutNanoTime();
|
||||
if (timeoutNanoTime < Long.MAX_VALUE)
|
||||
{
|
||||
long newTimeout = timeoutAt - System.nanoTime();
|
||||
long newTimeout = NanoTime.until(timeoutNanoTime);
|
||||
if (newTimeout > 0)
|
||||
{
|
||||
newRequest.timeout(newTimeout, TimeUnit.NANOSECONDS);
|
||||
|
|
|
@ -74,6 +74,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.util.thread.Sweeper;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -142,12 +143,13 @@ public class HttpClient extends ContainerLifeCycle
|
|||
private boolean tcpNoDelay = true;
|
||||
private boolean strictEventOrdering = false;
|
||||
private HttpField encodingField;
|
||||
private boolean removeIdleDestinations = false;
|
||||
private long destinationIdleTimeout;
|
||||
private String name = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
|
||||
private HttpCompliance httpCompliance = HttpCompliance.RFC7230;
|
||||
private String defaultRequestContentType = "application/octet-stream";
|
||||
private boolean useInputDirectByteBuffers = true;
|
||||
private boolean useOutputDirectByteBuffers = true;
|
||||
private Sweeper destinationSweeper;
|
||||
|
||||
/**
|
||||
* Creates a HttpClient instance that can perform HTTP/1.1 requests to non-TLS and TLS destinations.
|
||||
|
@ -222,7 +224,14 @@ public class HttpClient extends ContainerLifeCycle
|
|||
cookieStore = cookieManager.getCookieStore();
|
||||
|
||||
transport.setHttpClient(this);
|
||||
|
||||
super.doStart();
|
||||
|
||||
if (getDestinationIdleTimeout() > 0L)
|
||||
{
|
||||
destinationSweeper = new Sweeper(scheduler, 1000L);
|
||||
destinationSweeper.start();
|
||||
}
|
||||
}
|
||||
|
||||
private CookieManager newCookieManager()
|
||||
|
@ -233,6 +242,12 @@ public class HttpClient extends ContainerLifeCycle
|
|||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
if (destinationSweeper != null)
|
||||
{
|
||||
destinationSweeper.stop();
|
||||
destinationSweeper = null;
|
||||
}
|
||||
|
||||
decoderFactories.clear();
|
||||
handlers.clear();
|
||||
|
||||
|
@ -290,6 +305,11 @@ public class HttpClient extends ContainerLifeCycle
|
|||
return cookieManager;
|
||||
}
|
||||
|
||||
Sweeper getDestinationSweeper()
|
||||
{
|
||||
return destinationSweeper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the authentication store associated with this instance
|
||||
*/
|
||||
|
@ -529,21 +549,28 @@ public class HttpClient extends ContainerLifeCycle
|
|||
*/
|
||||
public HttpDestination resolveDestination(Origin origin)
|
||||
{
|
||||
return destinations.computeIfAbsent(origin, o ->
|
||||
return destinations.compute(origin, (k, v) ->
|
||||
{
|
||||
HttpDestination destination = getTransport().newHttpDestination(o);
|
||||
// Start the destination before it's published to other threads.
|
||||
addManaged(destination);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Created {}", destination);
|
||||
return destination;
|
||||
if (v == null || v.stale())
|
||||
{
|
||||
HttpDestination newDestination = getTransport().newHttpDestination(k);
|
||||
// Start the destination before it's published to other threads.
|
||||
addManaged(newDestination);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Created {}; existing: '{}'", newDestination, v);
|
||||
return newDestination;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean removeDestination(HttpDestination destination)
|
||||
{
|
||||
boolean removed = destinations.remove(destination.getOrigin(), destination);
|
||||
removeBean(destination);
|
||||
return destinations.remove(destination.getOrigin(), destination);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed {}; result: {}", destination, removed);
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -925,28 +952,6 @@ public class HttpClient extends ContainerLifeCycle
|
|||
this.maxRedirects = maxRedirects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether TCP_NODELAY is enabled
|
||||
* @deprecated use {@link ClientConnector#isTCPNoDelay()} instead
|
||||
*/
|
||||
@ManagedAttribute(value = "Whether the TCP_NODELAY option is enabled", name = "tcpNoDelay")
|
||||
@Deprecated
|
||||
public boolean isTCPNoDelay()
|
||||
{
|
||||
return tcpNoDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tcpNoDelay whether TCP_NODELAY is enabled
|
||||
* @see java.net.Socket#setTcpNoDelay(boolean)
|
||||
* @deprecated use {@link ClientConnector#setTCPNoDelay(boolean)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setTCPNoDelay(boolean tcpNoDelay)
|
||||
{
|
||||
this.tcpNoDelay = tcpNoDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the http compliance mode for parsing http responses.
|
||||
* The default http compliance level is {@link HttpCompliance#RFC7230} which is the latest HTTP/1.1 specification
|
||||
|
@ -1011,30 +1016,37 @@ public class HttpClient extends ContainerLifeCycle
|
|||
}
|
||||
|
||||
/**
|
||||
* @return whether destinations that have no connections should be removed
|
||||
* @see #setRemoveIdleDestinations(boolean)
|
||||
* The default value is 0
|
||||
* @return the time in ms after which idle destinations are removed
|
||||
* @see #setDestinationIdleTimeout(long)
|
||||
*/
|
||||
@ManagedAttribute("Whether idle destinations are removed")
|
||||
public boolean isRemoveIdleDestinations()
|
||||
@ManagedAttribute("The time in ms after which idle destinations are removed, disabled when zero or negative")
|
||||
public long getDestinationIdleTimeout()
|
||||
{
|
||||
return removeIdleDestinations;
|
||||
return destinationIdleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether destinations that have no connections (nor active nor idle) should be removed.
|
||||
* <p>
|
||||
* Applications typically make request to a limited number of destinations so keeping
|
||||
* destinations around is not a problem for the memory or the GC.
|
||||
* However, for applications that hit millions of different destinations (e.g. a spider
|
||||
* bot) it would be useful to be able to remove the old destinations that won't be visited
|
||||
* anymore and leave space for new destinations.
|
||||
* Whether destinations that have no connections (nor active nor idle) and no exchanges
|
||||
* should be removed after the specified timeout.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the specified {@code destinationIdleTimeout} is 0 or negative, then the destinations
|
||||
* are not removed.
|
||||
* </p>
|
||||
* <p>
|
||||
* Avoids accumulating destinations when applications (e.g. a spider bot or web crawler)
|
||||
* hit a lot of different destinations that won't be visited again.
|
||||
* </p>
|
||||
*
|
||||
* @param removeIdleDestinations whether destinations that have no connections should be removed
|
||||
* @see org.eclipse.jetty.client.DuplexConnectionPool
|
||||
* @param destinationIdleTimeout the time in ms after which idle destinations are removed
|
||||
*/
|
||||
public void setRemoveIdleDestinations(boolean removeIdleDestinations)
|
||||
public void setDestinationIdleTimeout(long destinationIdleTimeout)
|
||||
{
|
||||
this.removeIdleDestinations = removeIdleDestinations;
|
||||
if (isStarted())
|
||||
throw new IllegalStateException();
|
||||
this.destinationIdleTimeout = destinationIdleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1125,17 +1137,6 @@ public class HttpClient extends ContainerLifeCycle
|
|||
return encodingField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param host the host to normalize
|
||||
* @return the host itself
|
||||
* @deprecated no replacement, do not use it
|
||||
*/
|
||||
@Deprecated
|
||||
protected String normalizeHost(String host)
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
public static int normalizePort(String scheme, int port)
|
||||
{
|
||||
if (port > 0)
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.net.URI;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
|
@ -34,6 +33,7 @@ import org.eclipse.jetty.http.HttpVersion;
|
|||
import org.eclipse.jetty.io.CyclicTimeouts;
|
||||
import org.eclipse.jetty.util.Attachable;
|
||||
import org.eclipse.jetty.util.HttpCookieStore;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -48,13 +48,13 @@ public abstract class HttpConnection implements IConnection, Attachable
|
|||
private final RequestTimeouts requestTimeouts;
|
||||
private Object attachment;
|
||||
private int idleTimeoutGuard;
|
||||
private long idleTimeoutStamp;
|
||||
private long idleTimeoutNanoTime;
|
||||
|
||||
protected HttpConnection(HttpDestination destination)
|
||||
{
|
||||
this.destination = destination;
|
||||
this.requestTimeouts = new RequestTimeouts(destination.getHttpClient().getScheduler());
|
||||
this.idleTimeoutStamp = System.nanoTime();
|
||||
this.idleTimeoutNanoTime = NanoTime.now();
|
||||
}
|
||||
|
||||
public HttpClient getHttpClient()
|
||||
|
@ -121,7 +121,7 @@ public abstract class HttpConnection implements IConnection, Attachable
|
|||
try (AutoLock l = lock.lock())
|
||||
{
|
||||
--idleTimeoutGuard;
|
||||
idleTimeoutStamp = System.nanoTime();
|
||||
idleTimeoutNanoTime = NanoTime.now();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -267,7 +267,7 @@ public abstract class HttpConnection implements IConnection, Attachable
|
|||
{
|
||||
if (idleTimeoutGuard == 0)
|
||||
{
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTimeoutStamp);
|
||||
long elapsed = NanoTime.millisSince(idleTimeoutNanoTime);
|
||||
boolean idle = elapsed > idleTimeout / 2;
|
||||
if (idle)
|
||||
idleTimeoutGuard = -1;
|
||||
|
|
|
@ -34,20 +34,23 @@ import org.eclipse.jetty.io.CyclicTimeouts;
|
|||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.component.DumpableCollection;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.util.thread.Sweeper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ManagedObject
|
||||
public class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Callback, Dumpable
|
||||
public class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Callback, Dumpable, Sweeper.Sweepable
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpDestination.class);
|
||||
|
||||
|
@ -60,7 +63,10 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
|
|||
private final ClientConnectionFactory connectionFactory;
|
||||
private final HttpField hostField;
|
||||
private final RequestTimeouts requestTimeouts;
|
||||
private final AutoLock staleLock = new AutoLock();
|
||||
private ConnectionPool connectionPool;
|
||||
private boolean stale;
|
||||
private long activeNanoTime;
|
||||
|
||||
public HttpDestination(HttpClient client, Origin origin, boolean intrinsicallySecure)
|
||||
{
|
||||
|
@ -104,23 +110,71 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
|
|||
connectionPool.accept(connection);
|
||||
}
|
||||
|
||||
public boolean stale()
|
||||
{
|
||||
try (AutoLock l = staleLock.lock())
|
||||
{
|
||||
boolean stale = this.stale;
|
||||
if (!stale)
|
||||
this.activeNanoTime = NanoTime.now();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stale check done with result {} on {}", stale, this);
|
||||
return stale;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sweep()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Sweep check in progress on {}", this);
|
||||
boolean remove = false;
|
||||
try (AutoLock l = staleLock.lock())
|
||||
{
|
||||
boolean stale = exchanges.isEmpty() && connectionPool.isEmpty();
|
||||
if (!stale)
|
||||
{
|
||||
this.activeNanoTime = NanoTime.now();
|
||||
}
|
||||
else if (NanoTime.millisSince(activeNanoTime) >= getHttpClient().getDestinationIdleTimeout())
|
||||
{
|
||||
this.stale = true;
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
if (remove)
|
||||
{
|
||||
getHttpClient().removeDestination(this);
|
||||
LifeCycle.stop(this);
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Sweep check done with result {} on {}", remove, this);
|
||||
return remove;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
this.connectionPool = newConnectionPool(client);
|
||||
addBean(connectionPool, true);
|
||||
super.doStart();
|
||||
Sweeper sweeper = client.getBean(Sweeper.class);
|
||||
if (sweeper != null && connectionPool instanceof Sweeper.Sweepable)
|
||||
sweeper.offer((Sweeper.Sweepable)connectionPool);
|
||||
Sweeper connectionPoolSweeper = client.getBean(Sweeper.class);
|
||||
if (connectionPoolSweeper != null && connectionPool instanceof Sweeper.Sweepable)
|
||||
connectionPoolSweeper.offer((Sweeper.Sweepable)connectionPool);
|
||||
Sweeper destinationSweeper = getHttpClient().getDestinationSweeper();
|
||||
if (destinationSweeper != null)
|
||||
destinationSweeper.offer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
Sweeper sweeper = client.getBean(Sweeper.class);
|
||||
if (sweeper != null && connectionPool instanceof Sweeper.Sweepable)
|
||||
sweeper.remove((Sweeper.Sweepable)connectionPool);
|
||||
Sweeper destinationSweeper = getHttpClient().getDestinationSweeper();
|
||||
if (destinationSweeper != null)
|
||||
destinationSweeper.remove(this);
|
||||
Sweeper connectionPoolSweeper = client.getBean(Sweeper.class);
|
||||
if (connectionPoolSweeper != null && connectionPool instanceof Sweeper.Sweepable)
|
||||
connectionPoolSweeper.remove((Sweeper.Sweepable)connectionPool);
|
||||
super.doStop();
|
||||
removeBean(connectionPool);
|
||||
}
|
||||
|
@ -449,11 +503,7 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
|
|||
{
|
||||
boolean removed = connectionPool.remove(connection);
|
||||
|
||||
if (getHttpExchanges().isEmpty())
|
||||
{
|
||||
tryRemoveIdleDestination();
|
||||
}
|
||||
else if (removed)
|
||||
if (removed)
|
||||
{
|
||||
// Process queued requests that may be waiting.
|
||||
// We may create a connection that is not
|
||||
|
@ -478,22 +528,6 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
|
|||
{
|
||||
exchange.getRequest().abort(cause);
|
||||
}
|
||||
if (exchanges.isEmpty())
|
||||
tryRemoveIdleDestination();
|
||||
}
|
||||
|
||||
private void tryRemoveIdleDestination()
|
||||
{
|
||||
if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty())
|
||||
{
|
||||
// There is a race condition between this thread removing the destination
|
||||
// and another thread queueing a request to this same destination.
|
||||
// If this destination is removed, but the request queued, a new connection
|
||||
// will be opened, the exchange will be executed and eventually the connection
|
||||
// will idle timeout and be closed. Meanwhile a new destination will be created
|
||||
// in HttpClient and will be used for other requests.
|
||||
getHttpClient().removeDestination(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -507,16 +541,39 @@ public class HttpDestination extends ContainerLifeCycle implements Destination,
|
|||
return getOrigin().asString();
|
||||
}
|
||||
|
||||
@ManagedAttribute("For how long this destination has been idle in ms")
|
||||
public long getIdle()
|
||||
{
|
||||
if (getHttpClient().getDestinationIdleTimeout() <= 0L)
|
||||
return -1;
|
||||
try (AutoLock l = staleLock.lock())
|
||||
{
|
||||
return NanoTime.millisSince(activeNanoTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedAttribute("Whether this destinations is stale")
|
||||
public boolean isStale()
|
||||
{
|
||||
try (AutoLock l = staleLock.lock())
|
||||
{
|
||||
return this.stale;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[%s]@%x%s,queue=%d,pool=%s",
|
||||
return String.format("%s[%s]@%x%s,state=%s,queue=%d,pool=%s,stale=%b,idle=%d",
|
||||
HttpDestination.class.getSimpleName(),
|
||||
getOrigin(),
|
||||
hashCode(),
|
||||
proxy == null ? "" : "(via " + proxy + ")",
|
||||
getState(),
|
||||
getQueuedRequestCount(),
|
||||
getConnectionPool());
|
||||
getConnectionPool(),
|
||||
isStale(),
|
||||
getIdle());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -93,7 +93,7 @@ public class HttpExchange implements CyclicTimeouts.Expirable
|
|||
@Override
|
||||
public long getExpireNanoTime()
|
||||
{
|
||||
return request.getTimeoutAt();
|
||||
return request.getTimeoutNanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.client.api.Response;
|
|||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -309,10 +310,10 @@ public class HttpRedirector
|
|||
|
||||
// Adjust the timeout of the new request, taking into account the
|
||||
// timeout of the previous request and the time already elapsed.
|
||||
long timeoutAt = httpRequest.getTimeoutAt();
|
||||
if (timeoutAt < Long.MAX_VALUE)
|
||||
long timeoutNanoTime = httpRequest.getTimeoutNanoTime();
|
||||
if (timeoutNanoTime < Long.MAX_VALUE)
|
||||
{
|
||||
long newTimeout = timeoutAt - System.nanoTime();
|
||||
long newTimeout = NanoTime.until(timeoutNanoTime);
|
||||
if (newTimeout > 0)
|
||||
{
|
||||
redirect.timeout(newTimeout, TimeUnit.NANOSECONDS);
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.eclipse.jetty.http.HttpMethod;
|
|||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
public class HttpRequest implements Request
|
||||
{
|
||||
|
@ -75,7 +76,7 @@ public class HttpRequest implements Request
|
|||
private boolean versionExplicit;
|
||||
private long idleTimeout = -1;
|
||||
private long timeout;
|
||||
private long timeoutAt = Long.MAX_VALUE;
|
||||
private long timeoutNanoTime = Long.MAX_VALUE;
|
||||
private Content content;
|
||||
private boolean followRedirects;
|
||||
private List<HttpCookie> cookies;
|
||||
|
@ -799,16 +800,16 @@ public class HttpRequest implements Request
|
|||
{
|
||||
long timeout = getTimeout();
|
||||
if (timeout > 0)
|
||||
timeoutAt = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout);
|
||||
timeoutNanoTime = NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The nanoTime at which the timeout expires or {@link Long#MAX_VALUE} if there is no timeout.
|
||||
* @see #timeout(long, TimeUnit)
|
||||
*/
|
||||
long getTimeoutAt()
|
||||
long getTimeoutNanoTime()
|
||||
{
|
||||
return timeoutAt;
|
||||
return timeoutNanoTime;
|
||||
}
|
||||
|
||||
protected List<Response.ResponseListener> getResponseListeners()
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.eclipse.jetty.client.api.Response;
|
|||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.io.CyclicTimeout;
|
||||
import org.eclipse.jetty.io.CyclicTimeouts;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -67,7 +68,7 @@ public class TimeoutCompleteListener extends CyclicTimeout implements Response.C
|
|||
{
|
||||
if (requestTimeout.compareAndSet(null, request))
|
||||
{
|
||||
long delay = Math.max(0, timeoutAt - System.nanoTime());
|
||||
long delay = Math.max(0, NanoTime.until(timeoutAt));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scheduling timeout in {} ms for {} on {}", TimeUnit.NANOSECONDS.toMillis(delay), request, this);
|
||||
schedule(delay, TimeUnit.NANOSECONDS);
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.component.DumpableCollection;
|
||||
|
@ -115,7 +116,7 @@ public class ValidatingConnectionPool extends DuplexConnectionPool
|
|||
|
||||
private class Holder implements Runnable
|
||||
{
|
||||
private final long timestamp = System.nanoTime();
|
||||
private final long creationNanoTime = NanoTime.now();
|
||||
private final AtomicBoolean done = new AtomicBoolean();
|
||||
private final Connection connection;
|
||||
public Scheduler.Task task;
|
||||
|
@ -156,7 +157,7 @@ public class ValidatingConnectionPool extends DuplexConnectionPool
|
|||
{
|
||||
return String.format("%s[validationLeft=%dms]",
|
||||
connection,
|
||||
timeout - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - timestamp)
|
||||
timeout - NanoTime.millisSince(creationNanoTime)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.Blocker;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
|
@ -212,13 +213,12 @@ public class ConnectionPoolTest
|
|||
|
||||
private void run(CountDownLatch latch, int iterations, List<Throwable> failures)
|
||||
{
|
||||
long begin = System.nanoTime();
|
||||
long begin = NanoTime.now();
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
{
|
||||
test(failures);
|
||||
}
|
||||
long end = System.nanoTime();
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
|
||||
long elapsed = NanoTime.millisSince(begin);
|
||||
System.err.printf("%d requests in %d ms, %.3f req/s%n", iterations, elapsed, elapsed > 0 ? iterations * 1000D / elapsed : -1D);
|
||||
latch.countDown();
|
||||
}
|
||||
|
|
|
@ -19,12 +19,14 @@ import java.util.concurrent.RejectedExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Destination;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
@ -291,6 +293,9 @@ public class DuplexHttpDestinationTest extends AbstractHttpClientServerTest
|
|||
public void testDestinationIsRemoved(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new EmptyServerHandler());
|
||||
client.stop();
|
||||
client.setDestinationIdleTimeout(1000);
|
||||
client.start();
|
||||
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
|
@ -305,7 +310,7 @@ public class DuplexHttpDestinationTest extends AbstractHttpClientServerTest
|
|||
Destination destinationAfter = client.resolveDestination(request);
|
||||
assertSame(destinationBefore, destinationAfter);
|
||||
|
||||
client.setRemoveIdleDestinations(true);
|
||||
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> client.getDestinations().isEmpty());
|
||||
|
||||
request = client.newRequest(host, port)
|
||||
.scheme(scenario.getScheme())
|
||||
|
@ -323,21 +328,19 @@ public class DuplexHttpDestinationTest extends AbstractHttpClientServerTest
|
|||
public void testDestinationIsRemovedAfterConnectionError(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new EmptyServerHandler());
|
||||
client.stop();
|
||||
client.setDestinationIdleTimeout(1000);
|
||||
client.start();
|
||||
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
client.setRemoveIdleDestinations(true);
|
||||
assertTrue(client.getDestinations().isEmpty(), "Destinations of a fresh client must be empty");
|
||||
|
||||
server.stop();
|
||||
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
|
||||
assertThrows(Exception.class, request::send);
|
||||
|
||||
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(1);
|
||||
while (!client.getDestinations().isEmpty() && System.nanoTime() < deadline)
|
||||
{
|
||||
Thread.sleep(10);
|
||||
}
|
||||
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> client.getDestinations().isEmpty());
|
||||
assertTrue(client.getDestinations().isEmpty(), "Destination must be removed after connection error");
|
||||
}
|
||||
|
||||
|
@ -348,8 +351,8 @@ public class DuplexHttpDestinationTest extends AbstractHttpClientServerTest
|
|||
|
||||
private Connection await(Supplier<Connection> supplier, long time, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
while (unit.toNanos(time) > System.nanoTime() - start)
|
||||
long start = NanoTime.now();
|
||||
while (NanoTime.since(start) < unit.toNanos(time))
|
||||
{
|
||||
Connection connection = supplier.get();
|
||||
if (connection != null)
|
||||
|
|
|
@ -70,7 +70,6 @@ public class HttpClientProxyProtocolTest
|
|||
clientThreads.setName("client");
|
||||
client = new HttpClient();
|
||||
client.setExecutor(clientThreads);
|
||||
client.setRemoveIdleDestinations(false);
|
||||
client.start();
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ import org.eclipse.jetty.util.Callback;
|
|||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -87,6 +88,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
@ -119,9 +121,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
|
||||
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
|
||||
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
HttpConnectionOverHTTP connection = null;
|
||||
while (connection == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
|
||||
while (connection == null && NanoTime.secondsSince(start) < 5)
|
||||
{
|
||||
connection = (HttpConnectionOverHTTP)connectionPool.getIdleConnections().peek();
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
|
@ -512,6 +514,41 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testRetryWithDestinationIdleTimeoutEnabled(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new EmptyServerHandler());
|
||||
client.stop();
|
||||
client.setDestinationIdleTimeout(1000);
|
||||
client.setIdleTimeout(1000);
|
||||
client.setMaxConnectionsPerDestination(1);
|
||||
client.start();
|
||||
|
||||
try (StacklessLogging ignored = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
|
||||
{
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.path("/one")
|
||||
.send();
|
||||
|
||||
int idleTimeout = 100;
|
||||
Thread.sleep(idleTimeout * 2);
|
||||
|
||||
// After serving a request over a connection that hasn't timed out, serving a second
|
||||
// request with a shorter idle timeout will make the connection timeout immediately
|
||||
// after being taken out of the pool. This triggers the retry mechanism.
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.path("/two")
|
||||
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||
.send();
|
||||
}
|
||||
|
||||
// Wait for the sweeper to remove the idle HttpDestination.
|
||||
await().atMost(5, TimeUnit.SECONDS).until(() -> client.getDestinations().isEmpty());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testExchangeIsCompleteOnlyWhenBothRequestAndResponseAreComplete(Scenario scenario) throws Exception
|
||||
|
@ -550,7 +587,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
.file(file)
|
||||
.onRequestSuccess(request ->
|
||||
{
|
||||
requestTime.set(System.nanoTime());
|
||||
requestTime.set(NanoTime.now());
|
||||
latch.countDown();
|
||||
})
|
||||
.send(new Response.Listener.Adapter()
|
||||
|
@ -558,22 +595,22 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
responseTime.set(System.nanoTime());
|
||||
responseTime.set(NanoTime.now());
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
exchangeTime.set(System.nanoTime());
|
||||
exchangeTime.set(NanoTime.now());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
|
||||
assertTrue(requestTime.get() <= exchangeTime.get());
|
||||
assertTrue(responseTime.get() <= exchangeTime.get());
|
||||
assertTrue(NanoTime.isBeforeOrSame(requestTime.get(), exchangeTime.get()));
|
||||
assertTrue(NanoTime.isBeforeOrSame(responseTime.get(), exchangeTime.get()));
|
||||
|
||||
// Give some time to the server to consume the request content
|
||||
// This is just to avoid exception traces in the test output
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.eclipse.jetty.server.Response;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.Blocker;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -73,12 +74,7 @@ public class HttpClientUploadDuringServerShutdownTest
|
|||
chunk.release();
|
||||
if (chunk.isLast())
|
||||
break;
|
||||
long now = System.nanoTime();
|
||||
long sleep = TimeUnit.MICROSECONDS.toNanos(1);
|
||||
while (System.nanoTime() < now + sleep)
|
||||
{
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
NanoTime.spinWait(TimeUnit.MICROSECONDS.toNanos(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Demo Handler
|
||||
|
||||
[tags]
|
||||
demo
|
||||
handler
|
||||
|
||||
[depends]
|
||||
server
|
||||
|
||||
[files]
|
||||
basehome:modules/demo.d/demo-handler.xml|etc/demo-handler.xml
|
||||
maven://org.eclipse.jetty.demos/jetty-demo-handler/${jetty.version}/jar|lib/jetty-demo-handler.jar
|
||||
|
||||
[xml]
|
||||
etc/demo-handler.xml
|
||||
|
||||
[lib]
|
||||
lib/jetty-demo-handler.jar
|
|
@ -1,8 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure id="helloDemo" class="org.eclipse.jetty.server.handler.ContextHandler">
|
||||
<Set name="handler">
|
||||
<New class="org.eclipse.jetty.demo.HelloHandler" />
|
||||
</Set>
|
||||
</Configure>
|
||||
<Configure id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
|
||||
<Call name="addHandler">
|
||||
<Arg>
|
||||
<New id="helloHandler" class="org.eclipse.jetty.server.handler.ContextHandler">
|
||||
<Set name="handler">
|
||||
<New class="org.eclipse.jetty.demo.HelloHandler" />
|
||||
</Set>
|
||||
<Set name="contextPath">/demo-handler</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.server.HttpChannel;
|
|||
import org.eclipse.jetty.server.HttpStream;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.thread.Invocable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -67,7 +68,7 @@ public class HttpStreamOverFCGI implements HttpStream
|
|||
_generator = generator;
|
||||
_httpChannel = httpChannel;
|
||||
_id = id;
|
||||
_nanoTime = System.nanoTime();
|
||||
_nanoTime = NanoTime.now();
|
||||
}
|
||||
|
||||
public HttpChannel getHttpChannel()
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.concurrent.ConcurrentMap;
|
|||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -127,11 +128,13 @@ public class CachingContentFactory implements HttpContent.ContentFactory
|
|||
// Scan the entire cache and generate an ordered list by last accessed time.
|
||||
SortedSet<CachingHttpContent> sorted = new TreeSet<>((c1, c2) ->
|
||||
{
|
||||
if (c1._lastAccessed != c2._lastAccessed)
|
||||
return Long.compare(c1._lastAccessed, c2._lastAccessed);
|
||||
long delta = NanoTime.elapsed(c2._lastAccessed, c1._lastAccessed);
|
||||
if (delta != 0)
|
||||
return delta < 0 ? -1 : 1;
|
||||
|
||||
if (c1._contentLengthValue < c2._contentLengthValue)
|
||||
return -1;
|
||||
delta = c1._contentLengthValue - c2._contentLengthValue;
|
||||
if (delta != 0)
|
||||
return delta < 0 ? -1 : 1;
|
||||
|
||||
return c1._cacheKey.compareTo(c2._cacheKey);
|
||||
});
|
||||
|
@ -254,7 +257,7 @@ public class CachingContentFactory implements HttpContent.ContentFactory
|
|||
_cacheKey = key;
|
||||
_buffer = byteBuffer;
|
||||
_lastModifiedValue = Files.getLastModifiedTime(httpContent.getResource().getPath());
|
||||
_lastAccessed = System.nanoTime();
|
||||
_lastAccessed = NanoTime.now();
|
||||
}
|
||||
|
||||
long calculateSize()
|
||||
|
@ -287,7 +290,7 @@ public class CachingContentFactory implements HttpContent.ContentFactory
|
|||
FileTime lastModifiedTime = Files.getLastModifiedTime(_delegate.getResource().getPath());
|
||||
if (lastModifiedTime.equals(_lastModifiedValue))
|
||||
{
|
||||
_lastAccessed = System.nanoTime();
|
||||
_lastAccessed = NanoTime.now();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.TreeMap;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.Attributes;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -112,7 +113,7 @@ public class HttpCookie
|
|||
|
||||
this(name, value, domain, path, maxAge, httpOnly, secure, comment, version, Collections.singletonMap("SameSite", sameSite == null ? null : sameSite.getAttributeValue()));
|
||||
}
|
||||
|
||||
|
||||
public HttpCookie(String name, String value, String domain, String path, long maxAge, boolean httpOnly, boolean secure, String comment, int version, Map<String, String> attributes)
|
||||
{
|
||||
_name = name;
|
||||
|
@ -124,24 +125,24 @@ public class HttpCookie
|
|||
_secure = secure;
|
||||
_comment = comment;
|
||||
_version = version;
|
||||
_expiration = maxAge < 0 ? -1 : System.nanoTime() + TimeUnit.SECONDS.toNanos(maxAge);
|
||||
_expiration = maxAge < 0 ? -1 : NanoTime.now() + TimeUnit.SECONDS.toNanos(maxAge);
|
||||
_attributes = (attributes == null ? Collections.emptyMap() : attributes);
|
||||
}
|
||||
|
||||
|
||||
public HttpCookie(String name, String value, int version, Map<String, String> attributes)
|
||||
{
|
||||
_name = name;
|
||||
_value = value;
|
||||
_value = value;
|
||||
_version = version;
|
||||
_attributes = (attributes == null ? Collections.emptyMap() : new TreeMap<>(attributes));
|
||||
|
||||
|
||||
//remove all of the well-known attributes, leaving only those pass-through ones
|
||||
_domain = _attributes.remove("Domain");
|
||||
_path = _attributes.remove("Path");
|
||||
|
||||
String tmp = _attributes.remove("Max-Age");
|
||||
_maxAge = StringUtil.isBlank(tmp) ? -1L : Long.valueOf(tmp);
|
||||
_expiration = _maxAge < 0 ? -1 : System.nanoTime() + TimeUnit.SECONDS.toNanos(_maxAge);
|
||||
_expiration = _maxAge < 0 ? -1 : NanoTime.now() + TimeUnit.SECONDS.toNanos(_maxAge);
|
||||
_httpOnly = Boolean.parseBoolean(_attributes.remove("HttpOnly"));
|
||||
_secure = Boolean.parseBoolean(_attributes.remove("Secure"));
|
||||
_comment = _attributes.remove("Comment");
|
||||
|
@ -236,7 +237,7 @@ public class HttpCookie
|
|||
*/
|
||||
public boolean isExpired(long timeNanos)
|
||||
{
|
||||
return _expiration >= 0 && timeNanos >= _expiration;
|
||||
return _expiration != -1 && NanoTime.isBefore(_expiration, timeNanos);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,7 +432,7 @@ public class HttpCookie
|
|||
buf.append("; Secure");
|
||||
if (_httpOnly)
|
||||
buf.append("; HttpOnly");
|
||||
|
||||
|
||||
String sameSite = _attributes.get("SameSite");
|
||||
if (sameSite != null)
|
||||
{
|
||||
|
@ -440,7 +441,7 @@ public class HttpCookie
|
|||
}
|
||||
|
||||
//Add all other attributes
|
||||
_attributes.entrySet().stream().filter(e -> !"SameSite".equals(e.getKey())).forEach(e ->
|
||||
_attributes.entrySet().stream().filter(e -> !"SameSite".equals(e.getKey())).forEach(e ->
|
||||
{
|
||||
buf.append("; " + e.getKey() + "=");
|
||||
buf.append(e.getValue());
|
||||
|
@ -510,21 +511,21 @@ public class HttpCookie
|
|||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract the bare minimum of info from a Set-Cookie header string.
|
||||
*
|
||||
*
|
||||
* Ideally this method should not be necessary, however as java.net.HttpCookie
|
||||
* does not yet support generic attributes, we have to use it in a minimal
|
||||
* fashion. When it supports attributes, we could look at reverting to a
|
||||
* constructor on o.e.j.h.HttpCookie to take the set-cookie header string.
|
||||
*
|
||||
*
|
||||
* @param setCookieHeader the header as a string
|
||||
* @return a map containing the name, value, domain, path. max-age of the set cookie header
|
||||
*/
|
||||
public static Map<String, String> extractBasics(String setCookieHeader)
|
||||
{
|
||||
//Parse the bare minimum
|
||||
//Parse the bare minimum
|
||||
List<java.net.HttpCookie> cookies = java.net.HttpCookie.parse(setCookieHeader);
|
||||
if (cookies.size() != 1)
|
||||
return Collections.emptyMap();
|
||||
|
@ -537,10 +538,10 @@ public class HttpCookie
|
|||
fields.put("max-age", Long.toString(cookie.getMaxAge()));
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the Set-Cookie header represented as a string is for the name, domain and path given.
|
||||
*
|
||||
*
|
||||
* @param setCookieHeader a Set-Cookie header
|
||||
* @param name the cookie name to check
|
||||
* @param domain the cookie domain to check
|
||||
|
@ -549,18 +550,18 @@ public class HttpCookie
|
|||
*/
|
||||
public static boolean match(String setCookieHeader, String name, String domain, String path)
|
||||
{
|
||||
//Parse the bare minimum
|
||||
//Parse the bare minimum
|
||||
List<java.net.HttpCookie> cookies = java.net.HttpCookie.parse(setCookieHeader);
|
||||
if (cookies.size() != 1)
|
||||
return false;
|
||||
|
||||
|
||||
java.net.HttpCookie cookie = cookies.get(0);
|
||||
return match(cookie.getName(), cookie.getDomain(), cookie.getPath(), name, domain, path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the HttpCookie is for the given name, domain and path.
|
||||
*
|
||||
*
|
||||
* @param cookie the jetty HttpCookie to check
|
||||
* @param name the cookie name to check
|
||||
* @param domain the cookie domain to check
|
||||
|
@ -573,10 +574,10 @@ public class HttpCookie
|
|||
return false;
|
||||
return match(cookie.getName(), cookie.getDomain(), cookie.getPath(), name, domain, path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if all old parameters match the new parameters.
|
||||
*
|
||||
*
|
||||
* @param oldName
|
||||
* @param oldDomain
|
||||
* @param oldPath
|
||||
|
@ -594,7 +595,7 @@ public class HttpCookie
|
|||
}
|
||||
else if (!oldName.equals(newName))
|
||||
return false;
|
||||
|
||||
|
||||
if (oldDomain == null)
|
||||
{
|
||||
if (newDomain != null)
|
||||
|
@ -610,7 +611,7 @@ public class HttpCookie
|
|||
}
|
||||
else if (!oldPath.equals(newPath))
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -685,7 +686,7 @@ public class HttpCookie
|
|||
return _cookie;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that samesite is set on the cookie. If not, use a
|
||||
* context default value, if one has been set.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.StringTokenizer;
|
||||
|
@ -363,12 +364,19 @@ public class HttpField
|
|||
}
|
||||
|
||||
public String[] getValues()
|
||||
{
|
||||
List<String> values = getValueList();
|
||||
if (values == null)
|
||||
return null;
|
||||
return values.toArray(String[]::new);
|
||||
}
|
||||
|
||||
public List<String> getValueList()
|
||||
{
|
||||
if (_value == null)
|
||||
return null;
|
||||
|
||||
QuotedCSV list = new QuotedCSV(false, _value);
|
||||
return list.getValues().toArray(new String[list.size()]);
|
||||
return list.getValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
|||
import org.eclipse.jetty.http2.internal.HTTP2Session;
|
||||
import org.eclipse.jetty.http2.internal.HTTP2Stream;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
|
@ -215,21 +216,21 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
|
|||
|
||||
protected void onSessionStalled(Session session)
|
||||
{
|
||||
sessionStall.set(System.nanoTime());
|
||||
sessionStall.set(NanoTime.now());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session stalled {}", session);
|
||||
}
|
||||
|
||||
protected void onStreamStalled(Stream stream)
|
||||
{
|
||||
streamsStalls.put(stream, System.nanoTime());
|
||||
streamsStalls.put(stream, NanoTime.now());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stream stalled {}", stream);
|
||||
}
|
||||
|
||||
protected void onSessionUnstalled(Session session)
|
||||
{
|
||||
long stallTime = System.nanoTime() - sessionStall.getAndSet(0);
|
||||
long stallTime = NanoTime.since(sessionStall.getAndSet(0));
|
||||
sessionStallTime.addAndGet(stallTime);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session unstalled after {} ms {}", TimeUnit.NANOSECONDS.toMillis(stallTime), session);
|
||||
|
@ -240,7 +241,7 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
|
|||
Long time = streamsStalls.remove(stream);
|
||||
if (time != null)
|
||||
{
|
||||
long stallTime = System.nanoTime() - time;
|
||||
long stallTime = NanoTime.since(time);
|
||||
streamsStallTime.addAndGet(stallTime);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Stream unstalled after {} ms {}", TimeUnit.NANOSECONDS.toMillis(stallTime), stream);
|
||||
|
@ -253,7 +254,7 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
|
|||
long pastStallTime = sessionStallTime.get();
|
||||
long currentStallTime = sessionStall.get();
|
||||
if (currentStallTime != 0)
|
||||
currentStallTime = System.nanoTime() - currentStallTime;
|
||||
currentStallTime = NanoTime.since(currentStallTime);
|
||||
return TimeUnit.NANOSECONDS.toMillis(pastStallTime + currentStallTime);
|
||||
}
|
||||
|
||||
|
@ -261,8 +262,8 @@ public abstract class AbstractFlowControlStrategy implements FlowControlStrategy
|
|||
public long getStreamsStallTime()
|
||||
{
|
||||
long pastStallTime = streamsStallTime.get();
|
||||
long now = System.nanoTime();
|
||||
long currentStallTime = streamsStalls.values().stream().reduce(0L, (result, time) -> now - time);
|
||||
long now = NanoTime.now();
|
||||
long currentStallTime = streamsStalls.values().stream().reduce(0L, (result, time) -> NanoTime.elapsed(time, now));
|
||||
return TimeUnit.NANOSECONDS.toMillis(pastStallTime + currentStallTime);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
/**
|
||||
* <p>An implementation of {@link RateControl} that limits the number of
|
||||
|
@ -65,13 +66,13 @@ public class WindowRateControl implements RateControl
|
|||
@Override
|
||||
public boolean onEvent(Object event)
|
||||
{
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
while (true)
|
||||
{
|
||||
Long time = events.peek();
|
||||
if (time == null)
|
||||
break;
|
||||
if (now < time)
|
||||
if (NanoTime.isBefore(now, time))
|
||||
break;
|
||||
if (events.remove(time))
|
||||
size.decrementAndGet();
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.Queue;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
@ -68,6 +67,7 @@ import org.eclipse.jetty.util.Atomics;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.CountingCallback;
|
||||
import org.eclipse.jetty.util.MathUtils;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
|
@ -1016,7 +1016,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
|
|||
|
||||
private void notIdle()
|
||||
{
|
||||
streamsState.idleTime = System.nanoTime();
|
||||
streamsState.idleNanoTime = NanoTime.now();
|
||||
}
|
||||
|
||||
public void onFrame(Frame frame)
|
||||
|
@ -1474,7 +1474,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
|
|||
private final Queue<Slot> slots = new ArrayDeque<>();
|
||||
// Must be incremented with the lock held.
|
||||
private final AtomicLong streamCount = new AtomicLong();
|
||||
private long idleTime = System.nanoTime();
|
||||
private long idleNanoTime = NanoTime.now();
|
||||
private CloseState closed = CloseState.NOT_CLOSED;
|
||||
private Runnable zeroStreamsAction;
|
||||
private GoAwayFrame goAwayRecv;
|
||||
|
@ -1814,7 +1814,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session
|
|||
{
|
||||
case NOT_CLOSED ->
|
||||
{
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTime);
|
||||
long elapsed = NanoTime.millisSince(idleNanoTime);
|
||||
if (elapsed < endPoint.getIdleTimeout())
|
||||
return false;
|
||||
notify = true;
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.eclipse.jetty.io.CyclicTimeouts;
|
|||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.util.Attachable;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
|
@ -65,7 +66,7 @@ public class HTTP2Stream implements Stream, Attachable, Closeable, Callback, Dum
|
|||
private final AtomicReference<CloseState> closeState = new AtomicReference<>(CloseState.NOT_CLOSED);
|
||||
private final AtomicInteger sendWindow = new AtomicInteger();
|
||||
private final AtomicInteger recvWindow = new AtomicInteger();
|
||||
private final long timeStamp = System.nanoTime();
|
||||
private final long creationNanoTime = NanoTime.now();
|
||||
private final HTTP2Session session;
|
||||
private final int streamId;
|
||||
private final MetaData.Request request;
|
||||
|
@ -268,7 +269,7 @@ public class HTTP2Stream implements Stream, Attachable, Closeable, Callback, Dum
|
|||
{
|
||||
long idleTimeout = getIdleTimeout();
|
||||
if (idleTimeout > 0)
|
||||
expireNanoTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(idleTimeout);
|
||||
expireNanoTime = NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(idleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -852,7 +853,7 @@ public class HTTP2Stream implements Stream, Attachable, Closeable, Callback, Dum
|
|||
localReset,
|
||||
remoteReset,
|
||||
closeState,
|
||||
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - timeStamp),
|
||||
NanoTime.millisSince(creationNanoTime),
|
||||
attachment);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.eclipse.jetty.http2.internal.generator.PingGenerator;
|
|||
import org.eclipse.jetty.http2.internal.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
@ -133,7 +134,7 @@ public class PingGenerateParseTest
|
|||
parser.init(UnaryOperator.identity());
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
PingFrame ping = new PingFrame(System.nanoTime(), true);
|
||||
PingFrame ping = new PingFrame(NanoTime.now(), true);
|
||||
generator.generate(lease, ping);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package org.eclipse.jetty.http2.hpack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.http.DateGenerator;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -38,7 +37,7 @@ public class HpackTest
|
|||
{
|
||||
static final HttpField ServerJetty = new PreEncodedHttpField(HttpHeader.SERVER, "jetty");
|
||||
static final HttpField XPowerJetty = new PreEncodedHttpField(HttpHeader.X_POWERED_BY, "jetty");
|
||||
static final HttpField Date = new PreEncodedHttpField(HttpHeader.DATE, DateGenerator.formatDate(TimeUnit.NANOSECONDS.toMillis(System.nanoTime())));
|
||||
static final HttpField Date = new PreEncodedHttpField(HttpHeader.DATE, DateGenerator.formatDate(System.currentTimeMillis()));
|
||||
|
||||
@Test
|
||||
public void encodeDecodeResponseTest() throws Exception
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.eclipse.jetty.server.HttpChannel;
|
|||
import org.eclipse.jetty.server.HttpStream;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -51,7 +52,7 @@ public class HttpStreamOverHTTP2 implements HttpStream, HTTP2Channel.Server
|
|||
private final HTTP2ServerConnection _connection;
|
||||
private final HttpChannel _httpChannel;
|
||||
private final HTTP2Stream _stream;
|
||||
private final long _nanoTimeStamp;
|
||||
private final long _nanoTime;
|
||||
private Content.Chunk _chunk;
|
||||
private MetaData.Response _metaData;
|
||||
private boolean committed;
|
||||
|
@ -62,7 +63,7 @@ public class HttpStreamOverHTTP2 implements HttpStream, HTTP2Channel.Server
|
|||
_connection = connection;
|
||||
_httpChannel = httpChannel;
|
||||
_stream = stream;
|
||||
_nanoTimeStamp = System.nanoTime();
|
||||
_nanoTime = NanoTime.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,7 +75,7 @@ public class HttpStreamOverHTTP2 implements HttpStream, HTTP2Channel.Server
|
|||
@Override
|
||||
public long getNanoTimeStamp()
|
||||
{
|
||||
return _nanoTimeStamp;
|
||||
return _nanoTime;
|
||||
}
|
||||
|
||||
public Runnable onRequest(HeadersFrame frame)
|
||||
|
|
|
@ -63,6 +63,7 @@ import org.eclipse.jetty.server.Handler;
|
|||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -517,11 +518,10 @@ public class MaxConcurrentStreamsTest extends AbstractTest
|
|||
// Wait until TCP congested.
|
||||
assertTrue(clientEndPointLatch.await(5, TimeUnit.SECONDS));
|
||||
AbstractEndPoint clientEndPoint = clientEndPointRef.get();
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
while (!clientEndPoint.getWriteFlusher().isPending())
|
||||
{
|
||||
long elapsed = System.nanoTime() - start;
|
||||
assertThat(TimeUnit.NANOSECONDS.toSeconds(elapsed), Matchers.lessThan(15L));
|
||||
assertThat(NanoTime.secondsSince(start), Matchers.lessThan(15L));
|
||||
Thread.sleep(100);
|
||||
}
|
||||
// Wait for the selector to update the SelectionKey to OP_WRITE.
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
|||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.internal.HTTP2Session;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -119,14 +120,12 @@ public class SessionFailureTest extends AbstractTest
|
|||
assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
long start = System.nanoTime();
|
||||
long now = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
while (((HTTP2Session)session).getEndPoint().isOpen())
|
||||
{
|
||||
assertThat(TimeUnit.NANOSECONDS.toSeconds(now - start), lessThanOrEqualTo(5L));
|
||||
assertThat(NanoTime.secondsSince(start), lessThanOrEqualTo(5L));
|
||||
|
||||
Thread.sleep(10);
|
||||
now = System.nanoTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.server.Request;
|
|||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -100,7 +101,7 @@ public class SmallThreadPoolLoadTest extends AbstractTest
|
|||
}, iterations * factor, TimeUnit.MILLISECONDS);
|
||||
|
||||
long successes = 0;
|
||||
long begin = System.nanoTime();
|
||||
long begin = NanoTime.now();
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
{
|
||||
boolean success = test(session, latch);
|
||||
|
@ -109,10 +110,9 @@ public class SmallThreadPoolLoadTest extends AbstractTest
|
|||
}
|
||||
|
||||
assertTrue(latch.await(iterations, TimeUnit.SECONDS));
|
||||
long end = System.nanoTime();
|
||||
assertThat(successes, Matchers.greaterThan(0L));
|
||||
task.cancel();
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
|
||||
long elapsed = NanoTime.millisSince(begin);
|
||||
logger.info("{} requests in {} ms, {}/{} success/failure, {} req/s",
|
||||
iterations, elapsed,
|
||||
successes, iterations - successes,
|
||||
|
|
|
@ -78,6 +78,7 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -1080,11 +1081,10 @@ public class StreamResetTest extends AbstractTest
|
|||
|
||||
private void waitUntilTCPCongested(WriteFlusher flusher) throws TimeoutException, InterruptedException
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
while (!flusher.isPending())
|
||||
{
|
||||
long elapsed = System.nanoTime() - start;
|
||||
if (TimeUnit.NANOSECONDS.toSeconds(elapsed) > 15)
|
||||
if (NanoTime.secondsSince(start) > 15)
|
||||
throw new TimeoutException();
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.io.CyclicTimeouts;
|
|||
import org.eclipse.jetty.quic.common.QuicStreamEndPoint;
|
||||
import org.eclipse.jetty.util.Attachable;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.thread.Invocable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -109,7 +110,7 @@ public abstract class HTTP3Stream implements Stream, CyclicTimeouts.Expirable, A
|
|||
{
|
||||
long idleTimeout = getIdleTimeout();
|
||||
if (idleTimeout > 0)
|
||||
expireNanoTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(idleTimeout);
|
||||
expireNanoTime = NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(idleTimeout);
|
||||
}
|
||||
|
||||
boolean onIdleTimeout(TimeoutException timeout)
|
||||
|
@ -308,7 +309,7 @@ public abstract class HTTP3Stream implements Stream, CyclicTimeouts.Expirable, A
|
|||
hashCode(),
|
||||
getId(),
|
||||
hasDemand(),
|
||||
TimeUnit.NANOSECONDS.toMillis(expireNanoTime - System.nanoTime()),
|
||||
NanoTime.millisSince(expireNanoTime),
|
||||
getSession()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.eclipse.jetty.server.HttpChannel;
|
|||
import org.eclipse.jetty.server.HttpStream;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -45,7 +46,7 @@ public class HttpStreamOverHTTP3 implements HttpStream
|
|||
private static final Logger LOG = LoggerFactory.getLogger(HttpStreamOverHTTP3.class);
|
||||
|
||||
private final AutoLock lock = new AutoLock();
|
||||
private final long nanoTime = System.nanoTime();
|
||||
private final long nanoTime = NanoTime.now();
|
||||
private final ServerHTTP3StreamConnection connection;
|
||||
private final HttpChannel httpChannel;
|
||||
private final HTTP3StreamServer stream;
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.function.Consumer;
|
|||
import java.util.function.IntConsumer;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
|
@ -176,7 +177,7 @@ abstract class AbstractByteBufferPool implements ByteBufferPool
|
|||
private final int _capacity;
|
||||
private final int _maxSize;
|
||||
private final AtomicInteger _size;
|
||||
private final AtomicLong _lastUpdate = new AtomicLong(System.nanoTime());
|
||||
private final AtomicLong _lastUpdate = new AtomicLong(NanoTime.now());
|
||||
private final IntConsumer _memoryFunction;
|
||||
|
||||
@Deprecated
|
||||
|
@ -223,7 +224,7 @@ abstract class AbstractByteBufferPool implements ByteBufferPool
|
|||
|
||||
void resetUpdateTime()
|
||||
{
|
||||
_lastUpdate.lazySet(System.nanoTime());
|
||||
_lastUpdate.lazySet(NanoTime.now());
|
||||
}
|
||||
|
||||
public void clear()
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Objects;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
|
@ -208,10 +209,10 @@ public class ArrayByteBufferPool extends AbstractByteBufferPool implements Dumpa
|
|||
Bucket bucket = buckets[i];
|
||||
if (bucket.isEmpty())
|
||||
continue;
|
||||
long lastUpdate = bucket.getLastUpdate();
|
||||
if (lastUpdate < oldest)
|
||||
long lastUpdateNanoTime = bucket.getLastUpdate();
|
||||
if (oldest == Long.MAX_VALUE || NanoTime.isBefore(lastUpdateNanoTime, oldest))
|
||||
{
|
||||
oldest = lastUpdate;
|
||||
oldest = lastUpdateNanoTime;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.function.Consumer;
|
|||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Pool;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
|
@ -353,7 +354,7 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool,
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("evicting {} bytes from {} pools", excess, (direct ? "direct" : "heap"));
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
long totalClearedCapacity = 0L;
|
||||
|
||||
RetainedBucket[] buckets = direct ? _direct : _indirect;
|
||||
|
@ -413,8 +414,8 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool,
|
|||
{
|
||||
if (oldestEntry != null)
|
||||
{
|
||||
long entryAge = now - entry.getPooled().getLastUpdate();
|
||||
if (entryAge > now - oldestEntry.getPooled().getLastUpdate())
|
||||
long entryAge = NanoTime.elapsed(entry.getPooled().getLastUpdate(), now);
|
||||
if (entryAge > NanoTime.elapsed(oldestEntry.getPooled().getLastUpdate(), now))
|
||||
oldestEntry = entry;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.io;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -91,7 +92,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
*/
|
||||
public boolean schedule(long delay, TimeUnit units)
|
||||
{
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
long newTimeoutAt = now + units.toNanos(delay);
|
||||
|
||||
Wakeup newWakeup = null;
|
||||
|
@ -103,7 +104,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
|
||||
// Is the current wakeup good to use? ie before our timeout time?
|
||||
Wakeup wakeup = timeout._wakeup;
|
||||
if (wakeup == null || wakeup._at > newTimeoutAt)
|
||||
if (wakeup == null || NanoTime.isBefore(newTimeoutAt, wakeup._at))
|
||||
// No, we need an earlier wakeup.
|
||||
wakeup = newWakeup = new Wakeup(newTimeoutAt, wakeup);
|
||||
|
||||
|
@ -114,7 +115,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
LOG.debug("Installed timeout in {} ms, {} wake up in {} ms",
|
||||
units.toMillis(delay),
|
||||
newWakeup != null ? "new" : "existing",
|
||||
TimeUnit.NANOSECONDS.toMillis(wakeup._at - now));
|
||||
NanoTime.millisElapsed(now, wakeup._at));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -192,7 +193,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
return String.format("%s@%x:%dms,%s",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
TimeUnit.NANOSECONDS.toMillis(_at - System.nanoTime()),
|
||||
NanoTime.millisUntil(_at),
|
||||
_wakeup);
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +215,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
|
||||
private void schedule(long now)
|
||||
{
|
||||
_task.compareAndSet(null, _scheduler.schedule(this, _at - now, TimeUnit.NANOSECONDS));
|
||||
_task.compareAndSet(null, _scheduler.schedule(this, NanoTime.elapsed(now, _at), TimeUnit.NANOSECONDS));
|
||||
}
|
||||
|
||||
private void destroy()
|
||||
|
@ -227,7 +228,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
Wakeup newWakeup = null;
|
||||
boolean hasExpired = false;
|
||||
while (true)
|
||||
|
@ -258,7 +259,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
wakeup = wakeup._next;
|
||||
|
||||
Timeout newTimeout;
|
||||
if (timeout._at <= now)
|
||||
if (NanoTime.isBeforeOrSame(timeout._at, now))
|
||||
{
|
||||
// We have timed out!
|
||||
hasExpired = true;
|
||||
|
@ -268,7 +269,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
{
|
||||
// We have not timed out, but we are set to!
|
||||
// Is the current wakeup good to use? ie before our timeout time?
|
||||
if (wakeup == null || wakeup._at >= timeout._at)
|
||||
if (wakeup == null || NanoTime.isBefore(timeout._at, wakeup._at))
|
||||
// No, we need an earlier wakeup.
|
||||
wakeup = newWakeup = new Wakeup(timeout._at, wakeup);
|
||||
newTimeout = new Timeout(timeout._at, wakeup);
|
||||
|
@ -299,7 +300,7 @@ public abstract class CyclicTimeout implements Destroyable
|
|||
return String.format("%s@%x:%dms->%s",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
_at == MAX_VALUE ? _at : TimeUnit.NANOSECONDS.toMillis(_at - System.nanoTime()),
|
||||
_at == MAX_VALUE ? _at : NanoTime.millisUntil(_at),
|
||||
_next);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.Iterator;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -75,7 +76,7 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Timeouts check for {}", this);
|
||||
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
long earliest = Long.MAX_VALUE;
|
||||
// Reset the earliest timeout so we can expire again.
|
||||
// A concurrent call to schedule(long) may lose an
|
||||
|
@ -95,12 +96,12 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
|
|||
long expiresAt = expirable.getExpireNanoTime();
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Entity {} expires in {} ms for {}", expirable, TimeUnit.NANOSECONDS.toMillis(expiresAt - now), this);
|
||||
LOG.debug("Entity {} expires in {} ms for {}", expirable, NanoTime.millisElapsed(now, expiresAt), this);
|
||||
|
||||
if (expiresAt == -1)
|
||||
continue;
|
||||
|
||||
if (expiresAt <= now)
|
||||
if (NanoTime.isBeforeOrSame(expiresAt, now))
|
||||
{
|
||||
boolean remove = onExpired(expirable);
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -109,11 +110,12 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
|
|||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
earliest = Math.min(earliest, expiresAt);
|
||||
|
||||
earliest = Math.min(earliest, NanoTime.elapsed(now, expiresAt));
|
||||
}
|
||||
|
||||
if (earliest < Long.MAX_VALUE)
|
||||
schedule(earliest);
|
||||
schedule(now + earliest);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,12 +135,12 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
|
|||
// Schedule a timeout for the earliest entity that may expire.
|
||||
// When the timeout expires, scan the entities for the next
|
||||
// earliest entity that may expire, and reschedule a new timeout.
|
||||
long prevEarliest = earliestTimeout.getAndUpdate(t -> Math.min(t, expiresAt));
|
||||
long prevEarliest = earliestTimeout.getAndUpdate(t -> NanoTime.isBefore(t, expiresAt) ? t : expiresAt);
|
||||
long expires = expiresAt;
|
||||
while (expires < prevEarliest)
|
||||
while (NanoTime.isBefore(expires, prevEarliest))
|
||||
{
|
||||
// A new entity expires earlier than previous entities, schedule it.
|
||||
long delay = Math.max(0, expires - System.nanoTime());
|
||||
long delay = Math.max(0, NanoTime.until(expires));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Scheduling timeout in {} ms for {}", TimeUnit.NANOSECONDS.toMillis(delay), this);
|
||||
schedule(cyclicTimeout, delay, TimeUnit.NANOSECONDS);
|
||||
|
@ -168,9 +170,9 @@ public abstract class CyclicTimeouts<T extends CyclicTimeouts.Expirable> impleme
|
|||
{
|
||||
/**
|
||||
* <p>Returns the expiration time in nanoseconds.</p>
|
||||
* <p>The value to return must be calculated taking into account {@link System#nanoTime()},
|
||||
* <p>The value to return must be calculated taking into account the current nanoTime,
|
||||
* for example:</p>
|
||||
* {@code expireNanoTime = System.nanoTime() + timeoutNanos}
|
||||
* {@code expireNanoTime = NanoTime.now() + timeoutNanos}
|
||||
* <p>Returning {@link Long#MAX_VALUE} indicates that this entity does not expire.</p>
|
||||
*
|
||||
* @return the expiration time in nanoseconds, or {@link Long#MAX_VALUE} if this entity does not expire
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -35,7 +36,7 @@ public abstract class IdleTimeout
|
|||
private final Scheduler _scheduler;
|
||||
private final AtomicReference<Scheduler.Task> _timeout = new AtomicReference<>();
|
||||
private volatile long _idleTimeout;
|
||||
private volatile long _idleTimestamp = System.nanoTime();
|
||||
private volatile long _idleNanoTime = NanoTime.now();
|
||||
|
||||
/**
|
||||
* @param scheduler A scheduler used to schedule checks for the idle timeout.
|
||||
|
@ -55,7 +56,7 @@ public abstract class IdleTimeout
|
|||
*/
|
||||
public long getIdleFor()
|
||||
{
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - _idleTimestamp);
|
||||
return NanoTime.millisSince(_idleNanoTime);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +101,7 @@ public abstract class IdleTimeout
|
|||
*/
|
||||
public void notIdle()
|
||||
{
|
||||
_idleTimestamp = System.nanoTime();
|
||||
_idleNanoTime = NanoTime.now();
|
||||
}
|
||||
|
||||
private void idleCheck()
|
||||
|
@ -147,8 +148,8 @@ public abstract class IdleTimeout
|
|||
{
|
||||
if (isOpen())
|
||||
{
|
||||
long idleTimestamp = _idleTimestamp;
|
||||
long idleElapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTimestamp);
|
||||
long idleNanoTime = _idleNanoTime;
|
||||
long idleElapsed = NanoTime.millisSince(idleNanoTime);
|
||||
long idleTimeout = getIdleTimeout();
|
||||
long idleLeft = idleTimeout - idleElapsed;
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
package org.eclipse.jetty.io;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
/**
|
||||
* Extension of the {@link ArrayByteBufferPool} whose bucket sizes increase exponentially instead of linearly.
|
||||
* Each bucket will be double the size of the previous bucket, this decreases the amounts of buckets required
|
||||
|
@ -113,10 +115,10 @@ public class LogarithmicArrayByteBufferPool extends ArrayByteBufferPool
|
|||
Bucket bucket = buckets[i];
|
||||
if (bucket.isEmpty())
|
||||
continue;
|
||||
long lastUpdate = bucket.getLastUpdate();
|
||||
if (lastUpdate < oldest)
|
||||
long lastUpdateNanoTime = bucket.getLastUpdate();
|
||||
if (oldest == Long.MAX_VALUE || NanoTime.isBefore(lastUpdateNanoTime, oldest))
|
||||
{
|
||||
oldest = lastUpdate;
|
||||
oldest = lastUpdateNanoTime;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
|
@ -205,10 +206,10 @@ public class MappedByteBufferPool extends AbstractByteBufferPool implements Dump
|
|||
if (bucket.isEmpty())
|
||||
continue;
|
||||
|
||||
long lastUpdate = bucket.getLastUpdate();
|
||||
if (lastUpdate < oldest)
|
||||
long lastUpdateNanoTime = bucket.getLastUpdate();
|
||||
if (oldest == Long.MAX_VALUE || NanoTime.isBefore(lastUpdateNanoTime, oldest))
|
||||
{
|
||||
oldest = lastUpdate;
|
||||
oldest = lastUpdateNanoTime;
|
||||
index = entry.getKey();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
/**
|
||||
* <p>A pooled ByteBuffer which maintains a reference count that is
|
||||
|
@ -35,7 +36,7 @@ public class RetainableByteBuffer extends Retainable.ReferenceCounter
|
|||
{
|
||||
private final ByteBuffer buffer;
|
||||
private final Consumer<RetainableByteBuffer> releaser;
|
||||
private final AtomicLong lastUpdate = new AtomicLong(System.nanoTime());
|
||||
private final AtomicLong lastUpdate = new AtomicLong(NanoTime.now());
|
||||
|
||||
RetainableByteBuffer(ByteBuffer buffer, Consumer<RetainableByteBuffer> releaser)
|
||||
{
|
||||
|
@ -75,7 +76,7 @@ public class RetainableByteBuffer extends Retainable.ReferenceCounter
|
|||
boolean released = super.release();
|
||||
if (released)
|
||||
{
|
||||
lastUpdate.setOpaque(System.nanoTime());
|
||||
lastUpdate.setOpaque(NanoTime.now());
|
||||
releaser.accept(this);
|
||||
}
|
||||
return released;
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -283,7 +284,7 @@ public class ByteArrayEndPointTest
|
|||
assertEquals("test", BufferUtil.toString(buffer));
|
||||
|
||||
// Wait for a read timeout.
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
fcb = new FutureCallback();
|
||||
endp.fillInterested(fcb);
|
||||
try
|
||||
|
@ -295,7 +296,7 @@ public class ByteArrayEndPointTest
|
|||
{
|
||||
assertThat(t.getCause(), instanceOf(TimeoutException.class));
|
||||
}
|
||||
assertThat(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), greaterThan(halfIdleTimeout));
|
||||
assertThat(NanoTime.millisSince(start), greaterThan(halfIdleTimeout));
|
||||
assertThat("Endpoint open", endp.isOpen(), is(true));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.io;
|
|||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -26,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
public class CyclicTimeoutTest
|
||||
{
|
||||
private volatile boolean _expired;
|
||||
private ScheduledExecutorScheduler _timer = new ScheduledExecutorScheduler();
|
||||
private final ScheduledExecutorScheduler _timer = new ScheduledExecutorScheduler();
|
||||
private CyclicTimeout _timeout;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -132,11 +133,11 @@ public class CyclicTimeoutTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBusy() throws Exception
|
||||
public void testBusy()
|
||||
{
|
||||
long testUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(2000);
|
||||
assertTrue(_timeout.schedule(500, TimeUnit.MILLISECONDS));
|
||||
while (System.nanoTime() < testUntil)
|
||||
long start = NanoTime.now();
|
||||
while (NanoTime.secondsSince(start) < 2)
|
||||
_timeout.schedule(500, TimeUnit.MILLISECONDS);
|
||||
_timeout.cancel();
|
||||
assertFalse(_expired);
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
@ -231,25 +232,25 @@ public class CyclicTimeoutsTest
|
|||
return new ConstantExpirable(delay, unit);
|
||||
}
|
||||
|
||||
private final long expireNanos;
|
||||
private final long expireNanoTime;
|
||||
private final String asString;
|
||||
|
||||
private ConstantExpirable()
|
||||
{
|
||||
this.expireNanos = Long.MAX_VALUE;
|
||||
this.expireNanoTime = Long.MAX_VALUE;
|
||||
this.asString = "noexp";
|
||||
}
|
||||
|
||||
public ConstantExpirable(long delay, TimeUnit unit)
|
||||
{
|
||||
this.expireNanos = System.nanoTime() + unit.toNanos(delay);
|
||||
this.expireNanoTime = NanoTime.now() + unit.toNanos(delay);
|
||||
this.asString = String.valueOf(unit.toMillis(delay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExpireNanoTime()
|
||||
{
|
||||
return expireNanos;
|
||||
return expireNanoTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
|
||||
package org.eclipse.jetty.io;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -126,8 +126,8 @@ public class IdleTimeoutTest
|
|||
assertNull(_expired);
|
||||
_timeout.setIdleTimeout(100);
|
||||
|
||||
long start = System.nanoTime();
|
||||
while (_expired == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
|
||||
long start = NanoTime.now();
|
||||
while (_expired == null && NanoTime.secondsSince(start) < 5)
|
||||
{
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
|||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
@ -99,8 +100,8 @@ public class SocketChannelEndPointTest
|
|||
private CountDownLatch _lastEndPointLatch;
|
||||
|
||||
// Must be volatile or the test may fail spuriously
|
||||
private AtomicInteger _blockAt = new AtomicInteger(0);
|
||||
private AtomicInteger _writeCount = new AtomicInteger(1);
|
||||
private final AtomicInteger _blockAt = new AtomicInteger(0);
|
||||
private final AtomicInteger _writeCount = new AtomicInteger(1);
|
||||
|
||||
public void init(Scenario scenario) throws Exception
|
||||
{
|
||||
|
@ -152,10 +153,9 @@ public class SocketChannelEndPointTest
|
|||
|
||||
// wait for read timeout
|
||||
client.setSoTimeout(500);
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
assertThrows(SocketTimeoutException.class, () -> client.getInputStream().read());
|
||||
long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start;
|
||||
assertThat("timeout duration", duration, greaterThanOrEqualTo(400L));
|
||||
assertThat(NanoTime.millisSince(start), greaterThanOrEqualTo(400L));
|
||||
|
||||
// write then shutdown
|
||||
client.getOutputStream().write("Goodbye Cruel TLS".getBytes(StandardCharsets.UTF_8));
|
||||
|
@ -207,9 +207,9 @@ public class SocketChannelEndPointTest
|
|||
}
|
||||
|
||||
// wait for read timeout
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
assertThrows(SocketTimeoutException.class, () -> client.getInputStream().read());
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start >= 400);
|
||||
assertThat(NanoTime.millisSince(start), greaterThanOrEqualTo(400L));
|
||||
|
||||
// write then shutdown
|
||||
client.getOutputStream().write("Goodbye Cruel TLS".getBytes(StandardCharsets.UTF_8));
|
||||
|
@ -255,10 +255,9 @@ public class SocketChannelEndPointTest
|
|||
_lastEndPoint.setIdleTimeout(10 * specifiedTimeout);
|
||||
Thread.sleep((11 * specifiedTimeout) / 10);
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
assertThrows(SocketTimeoutException.class, () -> clientInputStream.read());
|
||||
int elapsed = (int)(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start);
|
||||
assertThat("Expected timeout", elapsed, greaterThanOrEqualTo(3 * specifiedTimeout / 4));
|
||||
long start = NanoTime.now();
|
||||
assertThrows(SocketTimeoutException.class, clientInputStream::read);
|
||||
assertThat(NanoTime.millisSince(start), greaterThanOrEqualTo(3L * specifiedTimeout / 4));
|
||||
|
||||
// write remaining characters
|
||||
clientOutputStream.write("90ABCDEF".getBytes(StandardCharsets.UTF_8));
|
||||
|
@ -294,7 +293,7 @@ public class SocketChannelEndPointTest
|
|||
BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());
|
||||
final CountDownLatch latch = new CountDownLatch(writes);
|
||||
final InputStream in = new BufferedInputStream(client.getInputStream());
|
||||
final long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
final long start = NanoTime.now();
|
||||
out.write(bytes);
|
||||
out.write(count);
|
||||
out.flush();
|
||||
|
@ -326,7 +325,7 @@ public class SocketChannelEndPointTest
|
|||
count1 = count1 * 10 + (b - '0');
|
||||
b = in.read();
|
||||
}
|
||||
last = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
last = NanoTime.now();
|
||||
|
||||
//if (latch.getCount()%1000==0)
|
||||
// System.out.println(writes-latch.getCount());
|
||||
|
@ -336,12 +335,11 @@ public class SocketChannelEndPointTest
|
|||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
|
||||
long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long now = NanoTime.now();
|
||||
System.err.println("count=" + count1);
|
||||
System.err.println("latch=" + latch.getCount());
|
||||
System.err.println("time=" + (now - start));
|
||||
System.err.println("last=" + (now - last));
|
||||
System.err.println("time=" + NanoTime.millisElapsed(start, now));
|
||||
System.err.println("last=" + NanoTime.millisElapsed(last, now));
|
||||
System.err.println("endp=" + _lastEndPoint);
|
||||
System.err.println("conn=" + _lastEndPoint.getConnection());
|
||||
|
||||
|
@ -677,7 +675,6 @@ public class SocketChannelEndPointTest
|
|||
// volatile int _blockAt = 0;
|
||||
ByteBuffer _in = BufferUtil.allocate(32 * 1024);
|
||||
ByteBuffer _out = BufferUtil.allocate(32 * 1024);
|
||||
long _last = -1;
|
||||
final CountDownLatch _latch;
|
||||
|
||||
public TestConnection(EndPoint endp, Executor executor, AtomicInteger blockAt, AtomicInteger writeCount)
|
||||
|
@ -742,7 +739,6 @@ public class SocketChannelEndPointTest
|
|||
EndPoint endPoint = getEndPoint();
|
||||
try
|
||||
{
|
||||
_last = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
boolean progress = true;
|
||||
while (progress)
|
||||
{
|
||||
|
|
|
@ -36,5 +36,21 @@
|
|||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</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-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.test.keystore;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.keystore.KeystoreGenerator;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class KeystoreGeneratorTest
|
||||
{
|
||||
private Server _server;
|
||||
private ServerConnector _connector;
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public KeystoreGeneratorTest()
|
||||
{
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void before() throws Exception
|
||||
{
|
||||
// Generate a test keystore.
|
||||
String password = "myKeystorePassword";
|
||||
File targetTestingDir = MavenTestingUtils.getTargetTestingDir();
|
||||
File myPassword = KeystoreGenerator.generateTestKeystore(targetTestingDir.getAbsolutePath(), password);
|
||||
assertTrue(myPassword.exists());
|
||||
|
||||
// Configure the SslContextFactory and HttpConnectionFactory to use the keystore.
|
||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||
sslContextFactory.setKeyStorePath(myPassword.getAbsolutePath());
|
||||
sslContextFactory.setKeyStorePassword(password);
|
||||
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString());
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration();
|
||||
SecureRequestCustomizer secureRequestCustomizer = new SecureRequestCustomizer();
|
||||
secureRequestCustomizer.setSniHostCheck(false);
|
||||
httpsConfig.addCustomizer(secureRequestCustomizer);
|
||||
HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpsConfig);
|
||||
|
||||
// Start the server.
|
||||
_server = new Server();
|
||||
_connector = new ServerConnector(_server, sslConnectionFactory, httpConnectionFactory);
|
||||
_server.addConnector(_connector);
|
||||
_server.setHandler(new Handler.Processor()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
response.setStatus(200);
|
||||
response.write(true, BufferUtil.toBuffer("success"), callback);
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
|
||||
// Configure the client.
|
||||
SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client();
|
||||
clientSslContextFactory.setTrustAll(true);
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
clientConnector.setSslContextFactory(clientSslContextFactory);
|
||||
_httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
|
||||
_httpClient.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() throws Exception
|
||||
{
|
||||
_httpClient.stop();
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception
|
||||
{
|
||||
ContentResponse response = _httpClient.GET("https://localhost:" + _connector.getLocalPort());
|
||||
assertThat(response.getStatus(), equalTo(HttpStatus.OK_200));
|
||||
assertThat(response.getContentAsString(), equalTo("success"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-core</artifactId>
|
||||
<version>12.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-proxy</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Jetty Core :: Proxy</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.proxy</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-client</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>jetty-http2-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-java-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>jetty-http2-client-transport</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
module org.eclipse.jetty.proxy
|
||||
{
|
||||
requires org.slf4j;
|
||||
requires transitive org.eclipse.jetty.client;
|
||||
requires transitive org.eclipse.jetty.server;
|
||||
|
||||
exports org.eclipse.jetty.proxy;
|
||||
}
|
|
@ -0,0 +1,717 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.proxy;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.HttpCookieStore;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>A {@link Handler} that can be used to implement a {@link Forward forward
|
||||
* proxy ("proxy")} or a {@link Reverse reverse proxy ("gateway")} as defined by
|
||||
* <a href="https://datatracker.ietf.org/doc/html/rfc7230#section-2.3">RFC 7230</a>.</p>
|
||||
* <p>This class uses {@link HttpClient} to send requests from the proxy to the server.</p>
|
||||
* <p>The {@code HttpClient} instance is either
|
||||
* {@link #setHttpClient(HttpClient) set explicitly}, or created implicitly.
|
||||
* To customize the implicit {@code HttpClient} instance, applications can
|
||||
* override {@link #newHttpClient()} and {@link #configureHttpClient(HttpClient)}.</p>
|
||||
*
|
||||
* @see Forward
|
||||
* @see Reverse
|
||||
*/
|
||||
public abstract class ProxyHandler extends Handler.Processor
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ProxyHandler.class);
|
||||
private static final String CLIENT_TO_PROXY_REQUEST_ATTRIBUTE = ProxyHandler.class.getName() + ".clientToProxyRequest";
|
||||
private static final EnumSet<HttpHeader> HOP_HEADERS = EnumSet.of(
|
||||
HttpHeader.CONNECTION,
|
||||
HttpHeader.KEEP_ALIVE,
|
||||
HttpHeader.PROXY_AUTHORIZATION,
|
||||
HttpHeader.PROXY_AUTHENTICATE,
|
||||
HttpHeader.PROXY_CONNECTION,
|
||||
HttpHeader.TRANSFER_ENCODING,
|
||||
HttpHeader.TE,
|
||||
HttpHeader.TRAILER,
|
||||
HttpHeader.UPGRADE
|
||||
);
|
||||
|
||||
private HttpClient httpClient;
|
||||
private String proxyToServerHost;
|
||||
private String viaHost;
|
||||
|
||||
public HttpClient getHttpClient()
|
||||
{
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
public void setHttpClient(HttpClient httpClient)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the proxy-to-server {@code Host} header value
|
||||
*/
|
||||
public String getProxyToServerHost()
|
||||
{
|
||||
return proxyToServerHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the value to use for the {@code Host} header in proxy-to-server requests.</p>
|
||||
* <p>If {@code null}, the client-to-proxy value is used.</p>
|
||||
*
|
||||
* @param host the proxy-to-server {@code Host} header value
|
||||
*/
|
||||
public void setProxyToServerHost(String host)
|
||||
{
|
||||
this.proxyToServerHost = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value to use for the {@code Via} header
|
||||
*/
|
||||
public String getViaHost()
|
||||
{
|
||||
return viaHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the value to use for the {@code Via} header in proxy-to-server requests.</p>
|
||||
* <p>If {@code null}, the local host name is used.</p>
|
||||
*
|
||||
* @param viaHost the value to use for the {@code Via} header
|
||||
*/
|
||||
public void setViaHost(String viaHost)
|
||||
{
|
||||
this.viaHost = viaHost;
|
||||
}
|
||||
|
||||
private static String viaHost()
|
||||
{
|
||||
try
|
||||
{
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
}
|
||||
catch (UnknownHostException x)
|
||||
{
|
||||
return "localhost";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
if (httpClient == null)
|
||||
setHttpClient(createHttpClient());
|
||||
addBean(httpClient, true);
|
||||
|
||||
if (viaHost == null)
|
||||
setViaHost(viaHost());
|
||||
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
private HttpClient createHttpClient()
|
||||
{
|
||||
HttpClient httpClient = newHttpClient();
|
||||
configureHttpClient(httpClient);
|
||||
LifeCycle.start(httpClient);
|
||||
httpClient.getContentDecoderFactories().clear();
|
||||
httpClient.getProtocolHandlers().clear();
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new {@link HttpClient} instance, by default with a thread
|
||||
* pool named {@code proxy-client} and with the
|
||||
* {@link HttpClientTransportDynamic dynamic transport} configured only
|
||||
* with HTTP/1.1.</p>
|
||||
*
|
||||
* @return a new {@code HttpClient} instance
|
||||
*/
|
||||
protected HttpClient newHttpClient()
|
||||
{
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
QueuedThreadPool proxyClientThreads = new QueuedThreadPool();
|
||||
proxyClientThreads.setName("proxy-client");
|
||||
clientConnector.setExecutor(proxyClientThreads);
|
||||
return new HttpClient(new HttpClientTransportDynamic(clientConnector));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Configures the {@link HttpClient} instance before it is started.</p>
|
||||
*
|
||||
* @param httpClient the {@code HttpClient} instance to configure
|
||||
*/
|
||||
protected void configureHttpClient(HttpClient httpClient)
|
||||
{
|
||||
httpClient.setFollowRedirects(false);
|
||||
httpClient.setCookieStore(new HttpCookieStore.Empty());
|
||||
}
|
||||
|
||||
protected static String requestId(Request clientToProxyRequest)
|
||||
{
|
||||
return String.valueOf(System.identityHashCode(clientToProxyRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Request clientToProxyRequest, Response proxyToClientResponse, Callback proxyToClientCallback)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("""
|
||||
{} received C2P request
|
||||
{}
|
||||
{}""",
|
||||
requestId(clientToProxyRequest),
|
||||
clientToProxyRequest,
|
||||
clientToProxyRequest.getHeaders());
|
||||
|
||||
HttpURI rewritten = rewriteHttpURI(clientToProxyRequest);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} URI rewrite {} => {}", requestId(clientToProxyRequest), clientToProxyRequest.getHttpURI(), rewritten);
|
||||
|
||||
var proxyToServerRequest = newProxyToServerRequest(clientToProxyRequest, rewritten);
|
||||
|
||||
copyRequestHeaders(clientToProxyRequest, proxyToServerRequest);
|
||||
|
||||
addProxyHeaders(clientToProxyRequest, proxyToServerRequest);
|
||||
|
||||
if (hasContent(clientToProxyRequest))
|
||||
{
|
||||
if (expects100Continue(clientToProxyRequest))
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
var proxyToServerRequestContent = newProxyToServerRequestContent(clientToProxyRequest, proxyToClientResponse, proxyToServerRequest);
|
||||
proxyToServerRequest.body(proxyToServerRequestContent);
|
||||
}
|
||||
}
|
||||
|
||||
sendProxyToServerRequest(clientToProxyRequest, proxyToServerRequest, proxyToClientResponse, proxyToClientCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Rewrites the client-to-proxy request URI to the proxy-to-server request URI.</p>
|
||||
*
|
||||
* @param clientToProxyRequest the client-to-proxy request
|
||||
* @return an {@code HttpURI} for the proxy-to-server request
|
||||
*/
|
||||
protected abstract HttpURI rewriteHttpURI(Request clientToProxyRequest);
|
||||
|
||||
protected org.eclipse.jetty.client.api.Request newProxyToServerRequest(Request clientToProxyRequest, HttpURI newHttpURI)
|
||||
{
|
||||
return getHttpClient().newRequest(newHttpURI.toURI())
|
||||
.method(clientToProxyRequest.getMethod())
|
||||
.attribute(CLIENT_TO_PROXY_REQUEST_ATTRIBUTE, clientToProxyRequest);
|
||||
}
|
||||
|
||||
protected void copyRequestHeaders(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest)
|
||||
{
|
||||
Set<String> headersToRemove = findConnectionHeaders(clientToProxyRequest);
|
||||
|
||||
for (HttpField clientToProxyRequestField : clientToProxyRequest.getHeaders())
|
||||
{
|
||||
HttpHeader clientToProxyRequestHeader = clientToProxyRequestField.getHeader();
|
||||
|
||||
if (HttpHeader.HOST == clientToProxyRequestHeader)
|
||||
{
|
||||
String host = getProxyToServerHost();
|
||||
if (host != null)
|
||||
{
|
||||
proxyToServerRequest.headers(headers -> headers.put(HttpHeader.HOST, host));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (HOP_HEADERS.contains(clientToProxyRequestHeader))
|
||||
continue;
|
||||
if (headersToRemove != null && headersToRemove.contains(clientToProxyRequestField.getLowerCaseName()))
|
||||
continue;
|
||||
|
||||
proxyToServerRequest.headers(headers -> headers.add(clientToProxyRequestField));
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> findConnectionHeaders(Request clientToProxyRequest)
|
||||
{
|
||||
// Any header listed by the Connection header must be removed:
|
||||
// http://tools.ietf.org/html/rfc7230#section-6.1.
|
||||
Set<String> hopHeaders = null;
|
||||
List<String> connectionHeaders = clientToProxyRequest.getHeaders().getValuesList(HttpHeader.CONNECTION);
|
||||
for (String value : connectionHeaders)
|
||||
{
|
||||
String[] values = value.split(",");
|
||||
for (String name : values)
|
||||
{
|
||||
name = name.trim().toLowerCase(Locale.ENGLISH);
|
||||
if (hopHeaders == null)
|
||||
hopHeaders = new HashSet<>();
|
||||
hopHeaders.add(name);
|
||||
}
|
||||
}
|
||||
return hopHeaders;
|
||||
}
|
||||
|
||||
protected void addProxyHeaders(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest)
|
||||
{
|
||||
addViaHeader(clientToProxyRequest, proxyToServerRequest);
|
||||
addForwardedHeader(clientToProxyRequest, proxyToServerRequest);
|
||||
}
|
||||
|
||||
protected void addViaHeader(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest)
|
||||
{
|
||||
String protocol = clientToProxyRequest.getConnectionMetaData().getProtocol();
|
||||
String[] parts = protocol.split("/", 2);
|
||||
// Retain only the version if the protocol is HTTP.
|
||||
String protocolPart = parts.length == 2 && "HTTP".equalsIgnoreCase(parts[0]) ? parts[1] : protocol;
|
||||
String viaHeaderValue = protocolPart + " " + getViaHost();
|
||||
proxyToServerRequest.headers(headers -> headers.computeField(HttpHeader.VIA, (header, viaFields) ->
|
||||
{
|
||||
if (viaFields == null || viaFields.isEmpty())
|
||||
return new HttpField(header, viaHeaderValue);
|
||||
String separator = ", ";
|
||||
String newValue = viaFields.stream()
|
||||
.flatMap(field -> Stream.of(field.getValues()))
|
||||
.filter(value -> !StringUtil.isBlank(value))
|
||||
.collect(Collectors.joining(separator));
|
||||
if (newValue.length() > 0)
|
||||
newValue += separator;
|
||||
newValue += viaHeaderValue;
|
||||
return new HttpField(HttpHeader.VIA, newValue);
|
||||
}));
|
||||
}
|
||||
|
||||
protected void addForwardedHeader(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest)
|
||||
{
|
||||
String byAttr = Request.getLocalAddr(clientToProxyRequest);
|
||||
String forAttr = Request.getRemoteAddr(clientToProxyRequest);
|
||||
String hostAttr = clientToProxyRequest.getHeaders().get(HttpHeader.HOST);
|
||||
String scheme = clientToProxyRequest.getHttpURI().getScheme();
|
||||
// Even if the request came through a secure channel, look at the original scheme if present.
|
||||
// For example, a client with a forward proxy may want to communicate in clear-text with the
|
||||
// server (so the scheme is http), but securely with the forward proxy (so isSecure() is true).
|
||||
String protoAttr = scheme == null ? (clientToProxyRequest.isSecure() ? "https" : "http") : scheme;
|
||||
String forwardedValue = "by=%s;for=%s;host=%s;proto=%s".formatted(
|
||||
QuotedStringTokenizer.quote(byAttr),
|
||||
QuotedStringTokenizer.quote(forAttr),
|
||||
QuotedStringTokenizer.quote(hostAttr),
|
||||
protoAttr
|
||||
);
|
||||
|
||||
proxyToServerRequest.headers(headers -> headers.computeField(HttpHeader.FORWARDED, (header, fields) ->
|
||||
{
|
||||
String newValue;
|
||||
if (fields == null || fields.isEmpty())
|
||||
{
|
||||
newValue = forwardedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
String separator = ", ";
|
||||
newValue = fields.stream()
|
||||
.flatMap(field -> field.getValueList().stream())
|
||||
.collect(Collectors.joining(separator));
|
||||
newValue += separator + forwardedValue;
|
||||
}
|
||||
return new HttpField(HttpHeader.FORWARDED, newValue);
|
||||
}));
|
||||
}
|
||||
|
||||
private boolean hasContent(Request clientToProxyRequest)
|
||||
{
|
||||
if (clientToProxyRequest.getLength() > 0)
|
||||
return true;
|
||||
HttpFields headers = clientToProxyRequest.getHeaders();
|
||||
return headers.get(HttpHeader.CONTENT_TYPE) != null ||
|
||||
headers.get(HttpHeader.TRANSFER_ENCODING) != null;
|
||||
}
|
||||
|
||||
private boolean expects100Continue(Request clientToProxyRequest)
|
||||
{
|
||||
return HttpHeaderValue.CONTINUE.is(clientToProxyRequest.getHeaders().get(HttpHeader.EXPECT));
|
||||
}
|
||||
|
||||
protected org.eclipse.jetty.client.api.Request.Content newProxyToServerRequestContent(Request clientToProxyRequest, Response proxyToClientResponse, org.eclipse.jetty.client.api.Request proxyToServerRequest)
|
||||
{
|
||||
return new ProxyRequestContent(clientToProxyRequest);
|
||||
}
|
||||
|
||||
protected void sendProxyToServerRequest(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, Response proxyToClientResponse, Callback proxyToClientCallback)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("""
|
||||
{} sending P2S request
|
||||
{}
|
||||
{}""",
|
||||
requestId(clientToProxyRequest),
|
||||
proxyToServerRequest,
|
||||
proxyToServerRequest.getHeaders());
|
||||
}
|
||||
proxyToServerRequest.send(newServerToProxyResponseListener(clientToProxyRequest, proxyToServerRequest, proxyToClientResponse, proxyToClientCallback));
|
||||
}
|
||||
|
||||
protected org.eclipse.jetty.client.api.Response.CompleteListener newServerToProxyResponseListener(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, Response proxyToClientResponse, Callback proxyToClientCallback)
|
||||
{
|
||||
return new ProxyResponseListener(clientToProxyRequest, proxyToServerRequest, proxyToClientResponse, proxyToClientCallback);
|
||||
}
|
||||
|
||||
protected HttpField filterServerToProxyResponseField(HttpField serverToProxyResponseField)
|
||||
{
|
||||
return serverToProxyResponseField;
|
||||
}
|
||||
|
||||
protected void onServerToProxyResponseFailure(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, org.eclipse.jetty.client.api.Response serverToProxyResponse, Response proxyToClientResponse, Callback proxyToClientCallback, Throwable failure)
|
||||
{
|
||||
int status = HttpStatus.BAD_GATEWAY_502;
|
||||
if (failure instanceof TimeoutException)
|
||||
status = HttpStatus.GATEWAY_TIMEOUT_504;
|
||||
Callback callback = new ProxyToClientResponseFailureCallback(clientToProxyRequest, proxyToServerRequest, serverToProxyResponse, proxyToClientResponse, proxyToClientCallback);
|
||||
Response.writeError(clientToProxyRequest, proxyToClientResponse, callback, status);
|
||||
}
|
||||
|
||||
protected void onProxyToClientResponseComplete(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, org.eclipse.jetty.client.api.Response serverToProxyResponse, Response proxyToClientResponse, Callback proxyToClientCallback)
|
||||
{
|
||||
proxyToClientCallback.succeeded();
|
||||
}
|
||||
|
||||
protected void onProxyToClientResponseFailure(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, org.eclipse.jetty.client.api.Response serverToProxyResponse, Response proxyToClientResponse, Callback proxyToClientCallback, Throwable failure)
|
||||
{
|
||||
// There is no point trying to write an error,
|
||||
// we already know we cannot write to the client.
|
||||
proxyToClientCallback.failed(failure);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A {@code ProxyHandler} that can be used to implement a forward proxy server.</p>
|
||||
* <p>Forward proxies are configured in client applications that use
|
||||
* {@link HttpClient} in this way:</p>
|
||||
* <pre>{@code
|
||||
* httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
* }</pre>
|
||||
*
|
||||
* @see org.eclipse.jetty.client.ProxyConfiguration
|
||||
* @see org.eclipse.jetty.client.HttpProxy
|
||||
* @see Reverse
|
||||
*/
|
||||
public static class Forward extends ProxyHandler
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>Applications that use this class should return the client-to-proxy
|
||||
* request URI, since clients will send the absolute URI of the server.</p>
|
||||
*
|
||||
* @param clientToProxyRequest the client-to-proxy request
|
||||
* @return the client-to-proxy request URI
|
||||
*/
|
||||
@Override
|
||||
protected HttpURI rewriteHttpURI(Request clientToProxyRequest)
|
||||
{
|
||||
return clientToProxyRequest.getHttpURI();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A {@code ProxyHandler} that can be used to implement a reverse proxy.</p>
|
||||
* <p>A reverse proxy must rewrite the client-to-proxy request URI into the
|
||||
* proxy-to-server request URI.
|
||||
* This can be done by providing a rewrite function to the constructor,
|
||||
* and/or override {@link #rewriteHttpURI(Request)}.</p>
|
||||
*
|
||||
* @see Forward
|
||||
*/
|
||||
public static class Reverse extends ProxyHandler
|
||||
{
|
||||
private final Function<Request, HttpURI> httpURIRewriter;
|
||||
|
||||
public Reverse(Function<Request, HttpURI> httpURIRewriter)
|
||||
{
|
||||
this.httpURIRewriter = httpURIRewriter;
|
||||
}
|
||||
|
||||
public Function<Request, HttpURI> getHttpURIRewriter()
|
||||
{
|
||||
return httpURIRewriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>Applications that use this class must provide a rewrite function
|
||||
* to the constructor.</p>
|
||||
* <p>The rewrite function rewrites the client-to-proxy request URI,
|
||||
* for example {@code http://example.com/path}, to the proxy-to-server
|
||||
* request URI, for example {@code http://backend1:8080/path}.</p>
|
||||
*
|
||||
* @param clientToProxyRequest the client-to-proxy request
|
||||
* @return the proxy-to-server request URI.
|
||||
*/
|
||||
@Override
|
||||
protected HttpURI rewriteHttpURI(Request clientToProxyRequest)
|
||||
{
|
||||
return getHttpURIRewriter().apply(clientToProxyRequest);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ProxyRequestContent implements org.eclipse.jetty.client.api.Request.Content
|
||||
{
|
||||
private final Request clientToProxyRequest;
|
||||
|
||||
public ProxyRequestContent(Request clientToProxyRequest)
|
||||
{
|
||||
this.clientToProxyRequest = clientToProxyRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength()
|
||||
{
|
||||
return clientToProxyRequest.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Content.Chunk read()
|
||||
{
|
||||
Content.Chunk chunk = clientToProxyRequest.read();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} read C2P content {}", requestId(clientToProxyRequest), chunk);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void demand(Runnable demandCallback)
|
||||
{
|
||||
clientToProxyRequest.demand(demandCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(Throwable failure)
|
||||
{
|
||||
clientToProxyRequest.fail(failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType()
|
||||
{
|
||||
return clientToProxyRequest.getHeaders().get(HttpHeader.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rewind()
|
||||
{
|
||||
// TODO: can this be delegated to the clientToProxyRequest?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected class ProxyResponseListener extends Callback.Completable implements org.eclipse.jetty.client.api.Response.Listener
|
||||
{
|
||||
private final Request clientToProxyRequest;
|
||||
private final org.eclipse.jetty.client.api.Request proxyToServerRequest;
|
||||
private final Response proxyToClientResponse;
|
||||
private final Callback proxyToClientCallback;
|
||||
|
||||
public ProxyResponseListener(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, Response proxyToClientResponse, Callback proxyToClientCallback)
|
||||
{
|
||||
this.clientToProxyRequest = clientToProxyRequest;
|
||||
this.proxyToServerRequest = proxyToServerRequest;
|
||||
this.proxyToClientResponse = proxyToClientResponse;
|
||||
this.proxyToClientCallback = proxyToClientCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBegin(org.eclipse.jetty.client.api.Response serverToProxyResponse)
|
||||
{
|
||||
proxyToClientResponse.setStatus(serverToProxyResponse.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(org.eclipse.jetty.client.api.Response serverToProxyResponse)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("""
|
||||
{} received S2P response
|
||||
{}
|
||||
{}""",
|
||||
requestId(clientToProxyRequest),
|
||||
serverToProxyResponse,
|
||||
serverToProxyResponse.getHeaders());
|
||||
}
|
||||
for (HttpField serverToProxyResponseField : serverToProxyResponse.getHeaders())
|
||||
{
|
||||
if (HOP_HEADERS.contains(serverToProxyResponseField.getHeader()))
|
||||
continue;
|
||||
HttpField newField = filterServerToProxyResponseField(serverToProxyResponseField);
|
||||
if (newField == null)
|
||||
continue;
|
||||
proxyToClientResponse.getHeaders().add(newField);
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("""
|
||||
{} sending P2C response
|
||||
{}
|
||||
{}""",
|
||||
requestId(clientToProxyRequest),
|
||||
proxyToClientResponse,
|
||||
proxyToClientResponse.getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(org.eclipse.jetty.client.api.Response serverToProxyResponse, ByteBuffer serverToProxyContent, Callback serverToProxyContentCallback)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} received S2P content {}", requestId(clientToProxyRequest), BufferUtil.toDetailString(serverToProxyContent));
|
||||
Callback callback = new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} succeeded to write P2C content {}", requestId(clientToProxyRequest), BufferUtil.toDetailString(serverToProxyContent));
|
||||
serverToProxyContentCallback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} failed to write P2C content {}", requestId(clientToProxyRequest), BufferUtil.toDetailString(serverToProxyContent), failure);
|
||||
serverToProxyContentCallback.failed(failure);
|
||||
// Cannot write towards the client, abort towards the server.
|
||||
serverToProxyResponse.abort(failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationType getInvocationType()
|
||||
{
|
||||
return InvocationType.NON_BLOCKING;
|
||||
}
|
||||
};
|
||||
proxyToClientResponse.write(false, serverToProxyContent, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(org.eclipse.jetty.client.api.Response serverToProxyResponse)
|
||||
{
|
||||
proxyToClientResponse.write(true, BufferUtil.EMPTY_BUFFER, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
if (result.isSucceeded())
|
||||
{
|
||||
// Wait for the last write to complete.
|
||||
whenComplete((r, failure) ->
|
||||
{
|
||||
if (failure == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} P2C response complete {}", requestId(clientToProxyRequest), proxyToClientResponse);
|
||||
onProxyToClientResponseComplete(clientToProxyRequest, proxyToServerRequest, result.getResponse(), proxyToClientResponse, proxyToClientCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} P2C response failure {}", requestId(clientToProxyRequest), proxyToClientResponse, failure);
|
||||
onProxyToClientResponseFailure(clientToProxyRequest, proxyToServerRequest, result.getResponse(), proxyToClientResponse, proxyToClientCallback, failure);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} S2P failure {}", requestId(clientToProxyRequest), result.getResponse(), result.getFailure());
|
||||
onServerToProxyResponseFailure(clientToProxyRequest, proxyToServerRequest, result.getResponse(), proxyToClientResponse, proxyToClientCallback, result.getFailure());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyToClientResponseFailureCallback implements Callback
|
||||
{
|
||||
private final Request clientToProxyRequest;
|
||||
private final org.eclipse.jetty.client.api.Request proxyToServerRequest;
|
||||
private final org.eclipse.jetty.client.api.Response serverToProxyResponse;
|
||||
private final Response proxyToClientResponse;
|
||||
private final Callback proxyToClientCallback;
|
||||
|
||||
private ProxyToClientResponseFailureCallback(Request clientToProxyRequest, org.eclipse.jetty.client.api.Request proxyToServerRequest, org.eclipse.jetty.client.api.Response serverToProxyResponse, Response proxyToClientResponse, Callback proxyToClientCallback)
|
||||
{
|
||||
this.clientToProxyRequest = clientToProxyRequest;
|
||||
this.proxyToServerRequest = proxyToServerRequest;
|
||||
this.serverToProxyResponse = serverToProxyResponse;
|
||||
this.proxyToClientResponse = proxyToClientResponse;
|
||||
this.proxyToClientCallback = proxyToClientCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} P2C response complete {}", requestId(clientToProxyRequest), proxyToClientResponse);
|
||||
onProxyToClientResponseComplete(clientToProxyRequest, proxyToServerRequest, serverToProxyResponse, proxyToClientResponse, proxyToClientCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} P2C response failure {}", requestId(clientToProxyRequest), proxyToClientResponse, x);
|
||||
onProxyToClientResponseFailure(clientToProxyRequest, proxyToServerRequest, serverToProxyResponse, proxyToClientResponse, proxyToClientCallback, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationType getInvocationType()
|
||||
{
|
||||
return InvocationType.NON_BLOCKING;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,591 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.proxy;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||
import org.eclipse.jetty.client.AbstractConnectionPool;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpDestination;
|
||||
import org.eclipse.jetty.client.HttpProxy;
|
||||
import org.eclipse.jetty.client.Origin;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Destination;
|
||||
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
|
||||
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.HTTP2Cipher;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.internal.ErrorCode;
|
||||
import org.eclipse.jetty.http2.internal.HTTP2Connection;
|
||||
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
|
||||
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.ConnectHandler;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ForwardProxyWithDynamicTransportTest
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ForwardProxyWithDynamicTransportTest.class);
|
||||
|
||||
private Server server;
|
||||
private ServerConnector serverConnector;
|
||||
private ServerConnector serverTLSConnector;
|
||||
private Server proxy;
|
||||
private ServerConnector proxyConnector;
|
||||
private ServerConnector proxyTLSConnector;
|
||||
private HTTP2Client http2Client;
|
||||
private HttpClient client;
|
||||
|
||||
private void start(Handler handler) throws Exception
|
||||
{
|
||||
startServer(handler);
|
||||
startProxy(new ConnectHandler());
|
||||
startClient();
|
||||
}
|
||||
|
||||
private void startServer(Handler handler) throws Exception
|
||||
{
|
||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||
sslContextFactory.setKeyStorePath("src/test/resources/keystore.p12");
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
sslContextFactory.setUseCipherSuitesOrder(true);
|
||||
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
|
||||
|
||||
QueuedThreadPool serverThreads = new QueuedThreadPool();
|
||||
serverThreads.setName("server");
|
||||
server = new Server(serverThreads);
|
||||
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
HttpConnectionFactory h1c = new HttpConnectionFactory(httpConfig);
|
||||
HTTP2CServerConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);
|
||||
serverConnector = new ServerConnector(server, 1, 1, h1c, h2c);
|
||||
server.addConnector(serverConnector);
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
HttpConnectionFactory h1 = new HttpConnectionFactory(httpsConfig);
|
||||
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
|
||||
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
|
||||
alpn.setDefaultProtocol(h1.getProtocol());
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
|
||||
serverTLSConnector = new ServerConnector(server, 1, 1, ssl, alpn, h2, h1, h2c);
|
||||
server.addConnector(serverTLSConnector);
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
LOG.info("Started server on :{} and :{}", serverConnector.getLocalPort(), serverTLSConnector.getLocalPort());
|
||||
}
|
||||
|
||||
private void startProxy(ConnectHandler connectHandler) throws Exception
|
||||
{
|
||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||
sslContextFactory.setKeyStorePath("src/test/resources/keystore.p12");
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
sslContextFactory.setUseCipherSuitesOrder(true);
|
||||
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
|
||||
|
||||
QueuedThreadPool proxyThreads = new QueuedThreadPool();
|
||||
proxyThreads.setName("proxy");
|
||||
proxy = new Server(proxyThreads);
|
||||
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
ConnectionFactory h1c = new HttpConnectionFactory(httpConfig);
|
||||
ConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);
|
||||
proxyConnector = new ServerConnector(proxy, 1, 1, h1c, h2c);
|
||||
proxy.addConnector(proxyConnector);
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
HttpConnectionFactory h1 = new HttpConnectionFactory(httpsConfig);
|
||||
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
|
||||
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
|
||||
alpn.setDefaultProtocol(h1.getProtocol());
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
|
||||
proxyTLSConnector = new ServerConnector(proxy, 1, 1, ssl, alpn, h2, h1, h2c);
|
||||
proxy.addConnector(proxyTLSConnector);
|
||||
proxy.setHandler(connectHandler);
|
||||
connectHandler.setHandler(new ProxyHandler.Forward()
|
||||
{
|
||||
@Override
|
||||
protected HttpClient newHttpClient()
|
||||
{
|
||||
QueuedThreadPool proxyClientThreads = new QueuedThreadPool();
|
||||
proxyClientThreads.setName("proxy-client");
|
||||
ClientConnector proxyClientConnector = new ClientConnector();
|
||||
proxyClientConnector.setSelectors(1);
|
||||
proxyClientConnector.setExecutor(proxyClientThreads);
|
||||
proxyClientConnector.setSslContextFactory(new SslContextFactory.Client(true));
|
||||
HTTP2Client proxyHTTP2Client = new HTTP2Client(proxyClientConnector);
|
||||
ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
|
||||
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(proxyHTTP2Client);
|
||||
return new HttpClient(new HttpClientTransportDynamic(proxyClientConnector, h1, http2));
|
||||
}
|
||||
});
|
||||
proxy.start();
|
||||
LOG.info("Started proxy on :{} and :{}", proxyConnector.getLocalPort(), proxyTLSConnector.getLocalPort());
|
||||
}
|
||||
|
||||
private void startClient() throws Exception
|
||||
{
|
||||
QueuedThreadPool clientThreads = new QueuedThreadPool();
|
||||
clientThreads.setName("client");
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
clientConnector.setSelectors(1);
|
||||
clientConnector.setExecutor(clientThreads);
|
||||
clientConnector.setSslContextFactory(new SslContextFactory.Client(true));
|
||||
http2Client = new HTTP2Client(clientConnector);
|
||||
ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
|
||||
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
|
||||
client = new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, http2));
|
||||
client.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose()
|
||||
{
|
||||
LifeCycle.stop(client);
|
||||
LifeCycle.stop(proxy);
|
||||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
public static java.util.stream.Stream<Arguments> proxyMatrix()
|
||||
{
|
||||
var h1 = List.of("http/1.1");
|
||||
var h2c = List.of("h2c");
|
||||
var h2 = List.of("h2");
|
||||
return java.util.stream.Stream.of(
|
||||
// HTTP/1.1 Proxy with HTTP/1.1 Server.
|
||||
Arguments.of(new Origin.Protocol(h1, false), false, HttpVersion.HTTP_1_1, false),
|
||||
Arguments.of(new Origin.Protocol(h1, false), false, HttpVersion.HTTP_1_1, true),
|
||||
Arguments.of(new Origin.Protocol(h1, false), true, HttpVersion.HTTP_1_1, false),
|
||||
Arguments.of(new Origin.Protocol(h1, false), true, HttpVersion.HTTP_1_1, true),
|
||||
// HTTP/1.1 Proxy with HTTP/2 Server.
|
||||
Arguments.of(new Origin.Protocol(h1, false), false, HttpVersion.HTTP_2, false),
|
||||
Arguments.of(new Origin.Protocol(h1, false), false, HttpVersion.HTTP_2, true),
|
||||
Arguments.of(new Origin.Protocol(h1, false), true, HttpVersion.HTTP_2, false),
|
||||
Arguments.of(new Origin.Protocol(h1, false), true, HttpVersion.HTTP_2, true)
|
||||
// HTTP/2 Proxy with HTTP/1.1 Server.
|
||||
// TODO: re-enable when HTTP/2 tunnel support is implemented
|
||||
// Arguments.of(new Origin.Protocol(h2c, false), false, HttpVersion.HTTP_1_1, false),
|
||||
// Arguments.of(new Origin.Protocol(h2c, false), false, HttpVersion.HTTP_1_1, true),
|
||||
// Arguments.of(new Origin.Protocol(h2, false), true, HttpVersion.HTTP_1_1, false),
|
||||
// Arguments.of(new Origin.Protocol(h2, false), true, HttpVersion.HTTP_1_1, true),
|
||||
// Arguments.of(new Origin.Protocol(h2, true), true, HttpVersion.HTTP_1_1, false),
|
||||
// Arguments.of(new Origin.Protocol(h2, true), true, HttpVersion.HTTP_1_1, true),
|
||||
// HTTP/2 Proxy with HTTP/2 Server.
|
||||
// TODO: re-enable when HTTP/2 tunnel support is implemented
|
||||
// Arguments.of(new Origin.Protocol(h2c, false), false, HttpVersion.HTTP_2, false),
|
||||
// Arguments.of(new Origin.Protocol(h2c, false), false, HttpVersion.HTTP_2, true),
|
||||
// Arguments.of(new Origin.Protocol(h2, false), true, HttpVersion.HTTP_2, false),
|
||||
// Arguments.of(new Origin.Protocol(h2, false), true, HttpVersion.HTTP_2, true),
|
||||
// Arguments.of(new Origin.Protocol(h2, true), true, HttpVersion.HTTP_2, false),
|
||||
// Arguments.of(new Origin.Protocol(h2, true), true, HttpVersion.HTTP_2, true)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "proxyProtocol={0}, proxySecure={1}, serverProtocol={2}, serverSecure={3}")
|
||||
@MethodSource("proxyMatrix")
|
||||
public void testProxy(Origin.Protocol proxyProtocol, boolean proxySecure, HttpVersion serverProtocol, boolean serverSecure) throws Exception
|
||||
{
|
||||
int status = HttpStatus.NO_CONTENT_204;
|
||||
start(new Handler.Processor()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
response.setStatus(status);
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
|
||||
int proxyPort = proxySecure ? proxyTLSConnector.getLocalPort() : proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy proxy = new HttpProxy(proxyAddress, proxySecure, proxyProtocol);
|
||||
client.getProxyConfiguration().getProxies().add(proxy);
|
||||
|
||||
String scheme = serverSecure ? "https" : "http";
|
||||
int serverPort = serverSecure ? serverTLSConnector.getLocalPort() : serverConnector.getLocalPort();
|
||||
ContentResponse response1 = client.newRequest("localhost", serverPort)
|
||||
.scheme(scheme)
|
||||
.version(serverProtocol)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertEquals(status, response1.getStatus());
|
||||
|
||||
// Make a second request to be sure it went through the same connection.
|
||||
ContentResponse response2 = client.newRequest("localhost", serverPort)
|
||||
.scheme(scheme)
|
||||
.version(serverProtocol)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertEquals(status, response2.getStatus());
|
||||
|
||||
List<Destination> destinations = client.getDestinations().stream()
|
||||
.filter(d -> d.getPort() == serverPort)
|
||||
.toList();
|
||||
assertEquals(1, destinations.size());
|
||||
HttpDestination destination = (HttpDestination)destinations.get(0);
|
||||
AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool();
|
||||
assertEquals(1, connectionPool.getConnectionCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("re-enable when HTTP/2 tunnel support is implemented")
|
||||
public void testHTTP2TunnelClosedByClient() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy proxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(proxy);
|
||||
|
||||
long idleTimeout = 1000;
|
||||
http2Client.setStreamIdleTimeout(idleTimeout);
|
||||
|
||||
String serverScheme = "http";
|
||||
int serverPort = serverConnector.getLocalPort();
|
||||
ContentResponse response = client.newRequest("localhost", serverPort)
|
||||
.scheme(serverScheme)
|
||||
.version(HttpVersion.HTTP_1_1)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
|
||||
// Client will close the HTTP2StreamEndPoint.
|
||||
Thread.sleep(2 * idleTimeout);
|
||||
|
||||
List<Destination> destinations = client.getDestinations().stream()
|
||||
.filter(d -> d.getPort() == serverPort)
|
||||
.toList();
|
||||
assertEquals(1, destinations.size());
|
||||
HttpDestination destination = (HttpDestination)destinations.get(0);
|
||||
AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool();
|
||||
assertEquals(0, connectionPool.getConnectionCount());
|
||||
|
||||
List<HTTP2Connection> serverConnections = proxyConnector.getConnectedEndPoints().stream()
|
||||
.map(EndPoint::getConnection)
|
||||
.map(HTTP2Connection.class::cast)
|
||||
.toList();
|
||||
assertEquals(1, serverConnections.size());
|
||||
assertTrue(serverConnections.get(0).getSession().getStreams().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProxyDown() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy httpProxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(httpProxy);
|
||||
proxy.stop();
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
.version(HttpVersion.HTTP_1_1)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send(result ->
|
||||
{
|
||||
assertTrue(result.isFailed());
|
||||
assertThat(result.getFailure(), Matchers.instanceOf(ConnectException.class));
|
||||
latch.countDown();
|
||||
});
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("re-enable when HTTP/2 tunnel support is implemented")
|
||||
public void testHTTP2TunnelHardClosedByProxy() throws Exception
|
||||
{
|
||||
startServer(new EmptyServerHandler());
|
||||
CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
startProxy(new ConnectHandler()
|
||||
{
|
||||
@Override
|
||||
protected void handleConnect(Request request, Response response, Callback callback, String serverAddress)
|
||||
{
|
||||
request.getConnectionMetaData().getConnection().getEndPoint().close();
|
||||
closeLatch.countDown();
|
||||
}
|
||||
});
|
||||
startClient();
|
||||
|
||||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy httpProxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(httpProxy);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
.version(HttpVersion.HTTP_1_1)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send(result ->
|
||||
{
|
||||
assertTrue(result.isFailed());
|
||||
assertThat(result.getFailure(), Matchers.instanceOf(ClosedChannelException.class));
|
||||
latch.countDown();
|
||||
});
|
||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
List<Destination> destinations = client.getDestinations().stream()
|
||||
.filter(d -> d.getPort() == proxyPort)
|
||||
.toList();
|
||||
assertEquals(1, destinations.size());
|
||||
HttpDestination destination = (HttpDestination)destinations.get(0);
|
||||
AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool();
|
||||
assertEquals(0, connectionPool.getConnectionCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("re-enable when HTTP/2 tunnel support is implemented")
|
||||
public void testHTTP2TunnelResetByClient() throws Exception
|
||||
{
|
||||
startServer(new EmptyServerHandler());
|
||||
CountDownLatch closeLatch = new CountDownLatch(2);
|
||||
startProxy(new ConnectHandler()
|
||||
{
|
||||
@Override
|
||||
protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap<String, Object> context)
|
||||
{
|
||||
return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context)
|
||||
{
|
||||
@Override
|
||||
protected void close(Throwable failure)
|
||||
{
|
||||
super.close(failure);
|
||||
closeLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext)
|
||||
{
|
||||
return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext)
|
||||
{
|
||||
@Override
|
||||
protected void close(Throwable failure)
|
||||
{
|
||||
super.close(failure);
|
||||
closeLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
startClient();
|
||||
|
||||
FuturePromise<Session> sessionPromise = new FuturePromise<>();
|
||||
http2Client.connect(new InetSocketAddress("localhost", proxyConnector.getLocalPort()), new Session.Listener() {}, sessionPromise);
|
||||
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
|
||||
String serverAddress = "localhost:" + serverConnector.getLocalPort();
|
||||
MetaData.ConnectRequest connect = new MetaData.ConnectRequest(HttpScheme.HTTP, new HostPortHttpField(serverAddress), null, HttpFields.EMPTY, null);
|
||||
HeadersFrame frame = new HeadersFrame(connect, null, false);
|
||||
FuturePromise<Stream> streamPromise = new FuturePromise<>();
|
||||
CountDownLatch tunnelLatch = new CountDownLatch(1);
|
||||
CountDownLatch responseLatch = new CountDownLatch(1);
|
||||
session.newStream(frame, streamPromise, new Stream.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
MetaData.Response response = (MetaData.Response)frame.getMetaData();
|
||||
if (response.getStatus() == HttpStatus.OK_200)
|
||||
tunnelLatch.countDown();
|
||||
stream.demand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataAvailable(Stream stream)
|
||||
{
|
||||
Stream.Data data = stream.readData();
|
||||
String response = BufferUtil.toString(data.frame().getData(), StandardCharsets.UTF_8);
|
||||
data.release();
|
||||
if (response.startsWith("HTTP/1.1 200"))
|
||||
responseLatch.countDown();
|
||||
}
|
||||
});
|
||||
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
||||
assertTrue(tunnelLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Tunnel is established, send a HTTP/1.1 request.
|
||||
String h1 = "GET / HTTP/1.1\r\n" +
|
||||
"Host: " + serverAddress + "\r\n" +
|
||||
"\r\n";
|
||||
stream.data(new DataFrame(stream.getId(), ByteBuffer.wrap(h1.getBytes(StandardCharsets.UTF_8)), false), Callback.NOOP);
|
||||
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Now reset the stream, tunnel must be closed.
|
||||
stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
|
||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("re-enable when HTTP/2 tunnel support is implemented")
|
||||
public void testHTTP2TunnelProxyStreamTimeout() throws Exception
|
||||
{
|
||||
startServer(new EmptyServerHandler());
|
||||
CountDownLatch closeLatch = new CountDownLatch(2);
|
||||
startProxy(new ConnectHandler()
|
||||
{
|
||||
@Override
|
||||
protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap<String, Object> context)
|
||||
{
|
||||
return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context)
|
||||
{
|
||||
@Override
|
||||
protected void close(Throwable failure)
|
||||
{
|
||||
super.close(failure);
|
||||
closeLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext)
|
||||
{
|
||||
return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), connectContext)
|
||||
{
|
||||
@Override
|
||||
protected void close(Throwable failure)
|
||||
{
|
||||
super.close(failure);
|
||||
closeLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
startClient();
|
||||
|
||||
long streamIdleTimeout = 1000;
|
||||
ConnectionFactory h2c = proxyConnector.getConnectionFactory("h2c");
|
||||
((HTTP2CServerConnectionFactory)h2c).setStreamIdleTimeout(streamIdleTimeout);
|
||||
|
||||
FuturePromise<Session> sessionPromise = new FuturePromise<>();
|
||||
http2Client.connect(new InetSocketAddress("localhost", proxyConnector.getLocalPort()), new Session.Listener() {}, sessionPromise);
|
||||
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
|
||||
String serverAddress = "localhost:" + serverConnector.getLocalPort();
|
||||
MetaData.ConnectRequest connect = new MetaData.ConnectRequest(HttpScheme.HTTP, new HostPortHttpField(serverAddress), null, HttpFields.EMPTY, null);
|
||||
HeadersFrame frame = new HeadersFrame(connect, null, false);
|
||||
FuturePromise<Stream> streamPromise = new FuturePromise<>();
|
||||
CountDownLatch tunnelLatch = new CountDownLatch(1);
|
||||
CountDownLatch responseLatch = new CountDownLatch(1);
|
||||
CountDownLatch resetLatch = new CountDownLatch(1);
|
||||
session.newStream(frame, streamPromise, new Stream.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
MetaData.Response response = (MetaData.Response)frame.getMetaData();
|
||||
if (response.getStatus() == HttpStatus.OK_200)
|
||||
tunnelLatch.countDown();
|
||||
stream.demand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataAvailable(Stream stream)
|
||||
{
|
||||
Stream.Data data = stream.readData();
|
||||
String response = BufferUtil.toString(data.frame().getData(), StandardCharsets.UTF_8);
|
||||
data.release();
|
||||
if (response.startsWith("HTTP/1.1 200"))
|
||||
responseLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset(Stream stream, ResetFrame frame, Callback callback)
|
||||
{
|
||||
resetLatch.countDown();
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
||||
assertTrue(tunnelLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Tunnel is established, send a HTTP/1.1 request.
|
||||
String h1 = "GET / HTTP/1.1\r\n" +
|
||||
"Host: " + serverAddress + "\r\n" +
|
||||
"\r\n";
|
||||
stream.data(new DataFrame(stream.getId(), ByteBuffer.wrap(h1.getBytes(StandardCharsets.UTF_8)), false), Callback.NOOP);
|
||||
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Wait until the proxy stream idle times out.
|
||||
Thread.sleep(2 * streamIdleTimeout);
|
||||
|
||||
// Client should see a RST_STREAM.
|
||||
assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
|
||||
// Tunnel must be closed.
|
||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private static class EmptyServerHandler extends Handler.Processor
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.proxy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
|
||||
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
|
||||
import org.eclipse.jetty.client.util.StringRequestContent;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
|
||||
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ReverseProxyTest
|
||||
{
|
||||
private Server server;
|
||||
private ServerConnector serverConnector;
|
||||
private Server proxy;
|
||||
private ServerConnector proxyConnector;
|
||||
private HttpClient client;
|
||||
|
||||
private void startServer(Handler handler) throws Exception
|
||||
{
|
||||
QueuedThreadPool serverPool = new QueuedThreadPool();
|
||||
serverPool.setName("server");
|
||||
server = new Server(serverPool);
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
serverConnector = new ServerConnector(server, 1, 1, new HttpConnectionFactory(httpConfig), new HTTP2CServerConnectionFactory(httpConfig));
|
||||
server.addConnector(serverConnector);
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
}
|
||||
|
||||
private void startProxy(Handler handler) throws Exception
|
||||
{
|
||||
QueuedThreadPool proxyPool = new QueuedThreadPool();
|
||||
proxyPool.setName("proxy");
|
||||
proxy = new Server(proxyPool);
|
||||
HttpConfiguration configuration = new HttpConfiguration();
|
||||
configuration.setSendDateHeader(false);
|
||||
configuration.setSendServerVersion(false);
|
||||
proxyConnector = new ServerConnector(proxy, 1, 1, new HttpConnectionFactory(configuration), new HTTP2CServerConnectionFactory(configuration));
|
||||
proxy.addConnector(proxyConnector);
|
||||
proxy.setHandler(handler);
|
||||
proxy.start();
|
||||
}
|
||||
|
||||
private void startClient() throws Exception
|
||||
{
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
QueuedThreadPool clientThreads = new QueuedThreadPool();
|
||||
clientThreads.setName("client");
|
||||
clientConnector.setExecutor(clientThreads);
|
||||
HTTP2Client http2Client = new HTTP2Client(clientConnector);
|
||||
client = new HttpClient(new HttpClientTransportDynamic(clientConnector, HttpClientConnectionFactory.HTTP11, new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client)));
|
||||
client.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose()
|
||||
{
|
||||
LifeCycle.stop(client);
|
||||
LifeCycle.stop(proxy);
|
||||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
private static List<HttpVersion> httpVersions()
|
||||
{
|
||||
return List.of(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("httpVersions")
|
||||
public void testSimple(HttpVersion httpVersion) throws Exception
|
||||
{
|
||||
String clientContent = "hello";
|
||||
String serverContent = "world";
|
||||
startServer(new Handler.Processor()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
String requestContent = Content.Source.asString(request);
|
||||
assertEquals(clientContent, requestContent);
|
||||
Content.Sink.write(response, true, serverContent, callback);
|
||||
}
|
||||
});
|
||||
|
||||
startProxy(new ProxyHandler.Reverse(clientToProxyRequest ->
|
||||
HttpURI.build(clientToProxyRequest.getHttpURI()).port(serverConnector.getLocalPort()))
|
||||
{
|
||||
@Override
|
||||
protected HttpClient newHttpClient()
|
||||
{
|
||||
ClientConnector proxyClientConnector = new ClientConnector();
|
||||
QueuedThreadPool proxyClientThreads = new QueuedThreadPool();
|
||||
proxyClientThreads.setName("proxy-client");
|
||||
proxyClientConnector.setExecutor(proxyClientThreads);
|
||||
HTTP2Client proxyHTTP2Client = new HTTP2Client(proxyClientConnector);
|
||||
return new HttpClient(new HttpClientTransportDynamic(proxyClientConnector, HttpClientConnectionFactory.HTTP11, new ClientConnectionFactoryOverHTTP2.HTTP2(proxyHTTP2Client)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected org.eclipse.jetty.client.api.Request newProxyToServerRequest(Request clientToProxyRequest, HttpURI newHttpURI)
|
||||
{
|
||||
return super.newProxyToServerRequest(clientToProxyRequest, newHttpURI)
|
||||
.version(httpVersion);
|
||||
}
|
||||
});
|
||||
|
||||
startClient();
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort())
|
||||
.version(httpVersion)
|
||||
.body(new StringRequestContent(clientContent))
|
||||
.send();
|
||||
assertEquals(serverContent, response.getContentAsString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.proxy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.server.handler.LEVEL=DEBUG
|
Binary file not shown.
|
@ -31,6 +31,7 @@ import org.eclipse.jetty.quic.common.QuicStreamEndPoint;
|
|||
import org.eclipse.jetty.quic.quiche.QuicheConnection;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
||||
/**
|
||||
|
@ -120,6 +121,6 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp
|
|||
{
|
||||
long idleTimeout = getIdleTimeout();
|
||||
if (idleTimeout > 0)
|
||||
expireNanoTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(idleTimeout);
|
||||
expireNanoTime = NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(idleTimeout);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,23 @@
|
|||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
|
||||
|
||||
<Call id="ResourceFactory" class="org.eclipse.jetty.util.resource.ResourceFactory" name="of">
|
||||
<Arg><Ref refid="Contexts" /></Arg>
|
||||
<Call id="baseResource" name="newResource">
|
||||
<Arg>
|
||||
<Call name="resolvePath" class="org.eclipse.jetty.xml.XmlConfiguration">
|
||||
<Arg><Property name="jetty.base"/></Arg>
|
||||
<Arg><Property name="jetty.wellknown.dir" default=".well-known"/></Arg>
|
||||
</Call>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
|
||||
<New id="WellKnownHandler" class="org.eclipse.jetty.server.handler.ContextHandler">
|
||||
<Call id="ResourceFactory" class="org.eclipse.jetty.util.resource.ResourceFactory" name="of">
|
||||
<Arg><Ref refid="WellKnownHandler" /></Arg>
|
||||
<Call id="baseResource" name="newResource">
|
||||
<Arg>
|
||||
<Call name="resolvePath" class="org.eclipse.jetty.xml.XmlConfiguration">
|
||||
<Arg><Property name="jetty.base"/></Arg>
|
||||
<Arg><Property name="jetty.wellknown.dir" default=".well-known"/></Arg>
|
||||
</Call>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
|
||||
<Set name="contextPath">/.well-known</Set>
|
||||
<Set name="baseResource">
|
||||
<Ref refid="baseResource"/>
|
||||
</Set>
|
||||
<Set name="handler">
|
||||
<New class="org.eclipse.jetty.server.handler.ResourceHandler">
|
||||
<Set name="baseResource"><Ref refid="baseResource"/></Set>
|
||||
|
|
|
@ -27,8 +27,6 @@ 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.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
|
@ -47,12 +45,35 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SecureRequestCustomizer.class);
|
||||
public static final String X509_CERT = "org.eclipse.jetty.server.x509_cert";
|
||||
public static final String CERTIFICATES = "org.eclipse.jetty.server.certificates";
|
||||
private String _sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session"; // TODO better name?
|
||||
private String _sslSessionDataAttribute = _sslSessionAttribute + "_data"; // TODO better name?
|
||||
/**
|
||||
* <p>The request attribute name to use to obtain the peer certificate
|
||||
* chain as an array of {@link X509Certificate} objects.</p>
|
||||
*/
|
||||
public static final String PEER_CERTIFICATES_ATTRIBUTE = "org.eclipse.jetty.server.certificates";
|
||||
/**
|
||||
* <p>The request attribute name to use to obtain the local certificate
|
||||
* as an {@link X509} object.</p>
|
||||
*/
|
||||
public static final String X509_ATTRIBUTE = "org.eclipse.jetty.server.x509";
|
||||
/**
|
||||
* <p>The default value of the request attribute name to use to obtain
|
||||
* the {@link SSLSession} object.</p>
|
||||
*
|
||||
* @see #setSslSessionAttribute(String)
|
||||
*/
|
||||
public static final String DEFAULT_SSL_SESSION_ATTRIBUTE = "org.eclipse.jetty.server.sslSession";
|
||||
/**
|
||||
* <p>The default value of the request attribute name to use to obtain
|
||||
* the {@link SslSessionData} object.</p>
|
||||
*
|
||||
* @see #getSslSessionDataAttribute()
|
||||
*/
|
||||
public static final String DEFAULT_SSL_SESSION_DATA_ATTRIBUTE = newSslSessionDataAttribute(DEFAULT_SSL_SESSION_ATTRIBUTE);
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SecureRequestCustomizer.class);
|
||||
|
||||
private String _sslSessionAttribute = DEFAULT_SSL_SESSION_ATTRIBUTE;
|
||||
private String _sslSessionDataAttribute = DEFAULT_SSL_SESSION_DATA_ATTRIBUTE;
|
||||
private boolean _sniRequired;
|
||||
private boolean _sniHostCheck;
|
||||
private long _stsMaxAge;
|
||||
|
@ -144,21 +165,21 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the Strict-Transport-Security max age.
|
||||
* Sets the Strict-Transport-Security max age in seconds.
|
||||
*
|
||||
* @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent.
|
||||
* @param stsMaxAgeSeconds the max age in seconds for the Strict-Transport-Security response header.
|
||||
* If less than zero then no Strict-Transport-Security response header is set.
|
||||
*/
|
||||
public void setStsMaxAge(long stsMaxAgeSeconds)
|
||||
{
|
||||
_stsMaxAge = stsMaxAgeSeconds;
|
||||
formatSTS();
|
||||
setStsMaxAge(stsMaxAgeSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to call {@link #setStsMaxAge(long)}
|
||||
* Sets the Strict-Transport-Security max age in the given time unit.
|
||||
*
|
||||
* @param period The period in units
|
||||
* @param units The {@link TimeUnit} of the period
|
||||
* @param period The max age value
|
||||
* @param units The {@link TimeUnit} of the max age
|
||||
*/
|
||||
public void setStsMaxAge(long period, TimeUnit units)
|
||||
{
|
||||
|
@ -167,7 +188,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
}
|
||||
|
||||
/**
|
||||
* @return true if a include subdomain property is sent with any Strict-Transport-Security header
|
||||
* @return whether the {@code includeSubdomains} attribute is sent with the Strict-Transport-Security response header
|
||||
*/
|
||||
public boolean isStsIncludeSubDomains()
|
||||
{
|
||||
|
@ -175,7 +196,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
}
|
||||
|
||||
/**
|
||||
* @param stsIncludeSubDomains If true, a include subdomain property is sent with any Strict-Transport-Security header
|
||||
* @param stsIncludeSubDomains whether the {@code includeSubdomains} attribute is sent with the Strict-Transport-Security response header
|
||||
*/
|
||||
public void setStsIncludeSubDomains(boolean stsIncludeSubDomains)
|
||||
{
|
||||
|
@ -185,49 +206,41 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
|
||||
private void formatSTS()
|
||||
{
|
||||
if (_stsMaxAge < 0)
|
||||
long stsMaxAge = getStsMaxAge();
|
||||
if (stsMaxAge < 0)
|
||||
_stsField = null;
|
||||
else
|
||||
_stsField = new PreEncodedHttpField(HttpHeader.STRICT_TRANSPORT_SECURITY, String.format("max-age=%d%s", _stsMaxAge, _stsIncludeSubDomains ? "; includeSubDomains" : ""));
|
||||
_stsField = new PreEncodedHttpField(HttpHeader.STRICT_TRANSPORT_SECURITY, String.format("max-age=%d%s", stsMaxAge, isStsIncludeSubDomains() ? "; includeSubDomains" : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request customize(Request request, HttpFields.Mutable responseHeaders)
|
||||
{
|
||||
EndPoint endp = request.getConnectionMetaData().getConnection().getEndPoint();
|
||||
HttpURI uri = request.getHttpURI();
|
||||
SSLEngine sslEngine;
|
||||
if (endp instanceof DecryptedEndPoint)
|
||||
EndPoint endPoint = request.getConnectionMetaData().getConnection().getEndPoint();
|
||||
if (endPoint instanceof DecryptedEndPoint sslEndPoint)
|
||||
{
|
||||
DecryptedEndPoint sslEndp = (DecryptedEndPoint)endp;
|
||||
SslConnection sslConnection = sslEndp.getSslConnection();
|
||||
uri = (HttpScheme.HTTPS.is(uri.getScheme()))
|
||||
? uri : HttpURI.build(uri).scheme(HttpScheme.HTTPS);
|
||||
sslEngine = sslConnection.getSSLEngine();
|
||||
SslConnection sslConnection = sslEndPoint.getSslConnection();
|
||||
SSLEngine sslEngine = sslConnection.getSSLEngine();
|
||||
request = newSecureRequest(request, sslEngine);
|
||||
}
|
||||
else if (endp instanceof ProxyConnectionFactory.ProxyEndPoint)
|
||||
else if (endPoint instanceof ProxyConnectionFactory.ProxyEndPoint proxyEndPoint)
|
||||
{
|
||||
ProxyConnectionFactory.ProxyEndPoint proxy = (ProxyConnectionFactory.ProxyEndPoint)endp;
|
||||
if (proxy.getAttribute(ProxyConnectionFactory.TLS_VERSION) == null)
|
||||
return request;
|
||||
uri = (HttpScheme.HTTPS.is(uri.getScheme()))
|
||||
? uri : HttpURI.build(uri).scheme(HttpScheme.HTTPS);
|
||||
sslEngine = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return request;
|
||||
if (proxyEndPoint.getAttribute(ProxyConnectionFactory.TLS_VERSION) != null)
|
||||
request = newSecureRequest(request, null);
|
||||
}
|
||||
|
||||
if (_stsField != null)
|
||||
responseHeaders.add(_stsField);
|
||||
|
||||
return newSecureRequest(request, uri, sslEngine);
|
||||
return request;
|
||||
}
|
||||
|
||||
protected SecureRequest newSecureRequest(Request request, HttpURI uri, SSLEngine sslEngine)
|
||||
protected Request newSecureRequest(Request request, SSLEngine sslEngine)
|
||||
{
|
||||
return new SecureRequest(request, uri, sslEngine);
|
||||
if (sslEngine != null)
|
||||
return new SecureRequestWithTLSData(request, sslEngine);
|
||||
else
|
||||
return new SecureRequest(request);
|
||||
}
|
||||
|
||||
private X509Certificate[] getCertChain(Connector connector, SSLSession sslSession)
|
||||
|
@ -249,7 +262,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
{
|
||||
Objects.requireNonNull(attribute);
|
||||
_sslSessionAttribute = attribute;
|
||||
_sslSessionDataAttribute = attribute + "_data";
|
||||
_sslSessionDataAttribute = newSslSessionDataAttribute(attribute);
|
||||
}
|
||||
|
||||
public String getSslSessionAttribute()
|
||||
|
@ -258,14 +271,16 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
}
|
||||
|
||||
/**
|
||||
* Get data belonging to the {@link SSLSession}.
|
||||
*
|
||||
* @return the SslSessionData
|
||||
* @return {@link #getSslSessionAttribute()} {@code + "Data"}
|
||||
*/
|
||||
public static SslSessionData getSslSessionData(SSLSession session)
|
||||
public String getSslSessionDataAttribute()
|
||||
{
|
||||
String key = SslSessionData.class.getName();
|
||||
return (SslSessionData)session.getValue(key);
|
||||
return _sslSessionDataAttribute;
|
||||
}
|
||||
|
||||
private static String newSslSessionDataAttribute(String sslSessionAttribute)
|
||||
{
|
||||
return sslSessionAttribute + "Data";
|
||||
}
|
||||
|
||||
protected void checkSni(Request request, SSLSession session)
|
||||
|
@ -291,14 +306,14 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
|
||||
private X509 getX509(SSLSession session)
|
||||
{
|
||||
X509 x509 = (X509)session.getValue(X509_CERT);
|
||||
X509 x509 = (X509)session.getValue(X509_ATTRIBUTE);
|
||||
if (x509 == null)
|
||||
{
|
||||
Certificate[] certificates = session.getLocalCertificates();
|
||||
if (certificates == null || certificates.length == 0 || !(certificates[0] instanceof X509Certificate))
|
||||
return null;
|
||||
x509 = new X509(null, (X509Certificate)certificates[0]);
|
||||
session.putValue(X509_CERT, x509);
|
||||
session.putValue(X509_ATTRIBUTE, x509);
|
||||
}
|
||||
return x509;
|
||||
}
|
||||
|
@ -309,57 +324,11 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
return String.format("%s@%x", this.getClass().getSimpleName(), hashCode());
|
||||
}
|
||||
|
||||
protected class SecureRequest extends Request.Wrapper
|
||||
protected static class SecureRequest extends Request.Wrapper
|
||||
{
|
||||
private final HttpURI _uri;
|
||||
private final SSLSession _sslSession;
|
||||
private final SslSessionData _sslSessionData;
|
||||
|
||||
private SecureRequest(Request request, HttpURI uri, SSLEngine sslEngine)
|
||||
public SecureRequest(Request wrapped)
|
||||
{
|
||||
super(request);
|
||||
_uri = uri.asImmutable();
|
||||
|
||||
if (sslEngine == null)
|
||||
{
|
||||
_sslSession = null;
|
||||
_sslSessionData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sslSession = sslEngine.getSession();
|
||||
checkSni(request, _sslSession);
|
||||
|
||||
String key = SslSessionData.class.getName();
|
||||
SslSessionData sslSessionData = (SslSessionData)_sslSession.getValue(key);
|
||||
if (sslSessionData == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
String cipherSuite = _sslSession.getCipherSuite();
|
||||
int keySize = SslContextFactory.deduceKeyLength(cipherSuite);
|
||||
|
||||
X509Certificate[] certs = getCertChain(getConnectionMetaData().getConnector(), _sslSession);
|
||||
|
||||
byte[] bytes = _sslSession.getId();
|
||||
String idStr = StringUtil.toHexString(bytes);
|
||||
|
||||
sslSessionData = new SslSessionData(keySize, certs, idStr);
|
||||
_sslSession.putValue(key, sslSessionData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to get secure details ", e);
|
||||
}
|
||||
}
|
||||
_sslSessionData = sslSessionData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpURI getHttpURI()
|
||||
{
|
||||
return _uri;
|
||||
super(wrapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -367,6 +336,43 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected class SecureRequestWithTLSData extends SecureRequest
|
||||
{
|
||||
private final SSLSession _sslSession;
|
||||
private final SslSessionData _sslSessionData;
|
||||
|
||||
public SecureRequestWithTLSData(Request request, SSLEngine sslEngine)
|
||||
{
|
||||
super(request);
|
||||
_sslSession = sslEngine.getSession();
|
||||
checkSni(request, _sslSession);
|
||||
|
||||
String key = SslSessionData.class.getName();
|
||||
SslSessionData sslSessionData = (SslSessionData)_sslSession.getValue(key);
|
||||
if (sslSessionData == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
String cipherSuite = _sslSession.getCipherSuite();
|
||||
int keySize = SslContextFactory.deduceKeyLength(cipherSuite);
|
||||
|
||||
X509Certificate[] certs = getCertChain(getConnectionMetaData().getConnector(), _sslSession);
|
||||
|
||||
byte[] bytes = _sslSession.getId();
|
||||
String idStr = StringUtil.toHexString(bytes);
|
||||
|
||||
sslSessionData = new SslSessionData(idStr, keySize, certs);
|
||||
_sslSession.putValue(key, sslSessionData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to get secure details ", e);
|
||||
}
|
||||
}
|
||||
_sslSessionData = sslSessionData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name)
|
||||
|
@ -376,14 +382,14 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
{
|
||||
if (name.equals(sessionAttribute))
|
||||
return _sslSession;
|
||||
if (name.equals(_sslSessionDataAttribute))
|
||||
if (name.equals(getSslSessionDataAttribute()))
|
||||
return _sslSessionData;
|
||||
}
|
||||
|
||||
return switch (name)
|
||||
{
|
||||
case X509_CERT -> getX509(_sslSession);
|
||||
case CERTIFICATES -> _sslSessionData._certs;
|
||||
case X509_ATTRIBUTE -> getX509(_sslSession);
|
||||
case PEER_CERTIFICATES_ATTRIBUTE -> _sslSessionData != null ? _sslSessionData.peerCertificates() : null;
|
||||
default -> super.getAttribute(name);
|
||||
};
|
||||
}
|
||||
|
@ -405,32 +411,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
|
|||
/**
|
||||
* Simple bundle of data that is cached in the SSLSession.
|
||||
*/
|
||||
public static class SslSessionData
|
||||
public record SslSessionData(String sessionId, int keySize, X509Certificate[] peerCertificates)
|
||||
{
|
||||
private final Integer _keySize;
|
||||
private final X509Certificate[] _certs;
|
||||
private final String _idStr;
|
||||
|
||||
private SslSessionData(Integer keySize, X509Certificate[] certs, String idStr)
|
||||
{
|
||||
this._keySize = keySize;
|
||||
this._certs = certs;
|
||||
this._idStr = idStr;
|
||||
}
|
||||
|
||||
public Integer getKeySize()
|
||||
{
|
||||
return _keySize;
|
||||
}
|
||||
|
||||
public X509Certificate[] getX509Certificates()
|
||||
{
|
||||
return _certs;
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return _idStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.eclipse.jetty.util.Attributes;
|
|||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.ExceptionUtil;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.Uptime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.Name;
|
||||
|
@ -495,7 +496,7 @@ public class Server extends Handler.Wrapper implements Attributes
|
|||
|
||||
if (getStopTimeout() > 0)
|
||||
{
|
||||
long end = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(getStopTimeout());
|
||||
long end = NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(getStopTimeout());
|
||||
try
|
||||
{
|
||||
Graceful.shutdown(this).get(getStopTimeout(), TimeUnit.MILLISECONDS);
|
||||
|
@ -506,7 +507,7 @@ public class Server extends Handler.Wrapper implements Attributes
|
|||
}
|
||||
QueuedThreadPool qtp = getBean(QueuedThreadPool.class);
|
||||
if (qtp != null)
|
||||
qtp.setStopTimeout(Math.max(1000L, TimeUnit.NANOSECONDS.toMillis(end - System.nanoTime())));
|
||||
qtp.setStopTimeout(Math.max(1000L, NanoTime.millisUntil(end)));
|
||||
}
|
||||
|
||||
// Now stop the connectors (this will close existing connections)
|
||||
|
@ -747,7 +748,7 @@ public class Server extends Handler.Wrapper implements Attributes
|
|||
{
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run(Runnable runnable, Request request)
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
@ -39,6 +39,11 @@ import org.eclipse.jetty.io.ManagedSelector;
|
|||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.io.SelectorManager;
|
||||
import org.eclipse.jetty.io.SocketChannelEndPoint;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpStream;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.TunnelSupport;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
|
@ -28,6 +28,7 @@ import org.eclipse.jetty.server.Request;
|
|||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
import org.eclipse.jetty.util.statistic.CounterStatistic;
|
||||
|
@ -53,7 +54,7 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
long beginTimeStamp = System.nanoTime();
|
||||
long beginNanoTime = NanoTime.now();
|
||||
_handleStats.increment();
|
||||
StatisticsRequest statisticsRequest = new StatisticsRequest(request);
|
||||
|
||||
|
@ -69,7 +70,7 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
finally
|
||||
{
|
||||
_handleStats.decrement();
|
||||
_handleTimeStats.record(System.nanoTime() - beginTimeStamp);
|
||||
_handleTimeStats.record(NanoTime.since(beginNanoTime));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,7 +253,7 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
{
|
||||
private final LongAdder _bytesRead = new LongAdder();
|
||||
private final LongAdder _bytesWritten = new LongAdder();
|
||||
private long _processStartTimeStamp;
|
||||
private long _processStartNanoTime;
|
||||
|
||||
private StatisticsRequest(Request request)
|
||||
{
|
||||
|
@ -283,13 +284,13 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
|
||||
private long spentTimeNs()
|
||||
{
|
||||
return System.nanoTime() - _processStartTimeStamp;
|
||||
return NanoTime.since(_processStartNanoTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Request ignored, Response response, Callback callback) throws Exception
|
||||
{
|
||||
_processStartTimeStamp = System.nanoTime();
|
||||
_processStartNanoTime = NanoTime.now();
|
||||
_processStats.increment();
|
||||
_requestStats.increment();
|
||||
|
||||
|
@ -342,7 +343,7 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
public void succeeded()
|
||||
{
|
||||
_requestStats.decrement();
|
||||
_requestTimeStats.record(System.nanoTime() - getNanoTimeStamp());
|
||||
_requestTimeStats.record(NanoTime.since(getNanoTimeStamp()));
|
||||
super.succeeded();
|
||||
}
|
||||
|
||||
|
@ -350,7 +351,7 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
public void failed(Throwable x)
|
||||
{
|
||||
_requestStats.decrement();
|
||||
_requestTimeStats.record(System.nanoTime() - getNanoTimeStamp());
|
||||
_requestTimeStats.record(NanoTime.since(getNanoTimeStamp()));
|
||||
super.failed(x);
|
||||
}
|
||||
});
|
||||
|
@ -367,7 +368,7 @@ public class StatisticsHandler extends Handler.Wrapper
|
|||
finally
|
||||
{
|
||||
_processStats.decrement();
|
||||
_processTimeStats.record(System.nanoTime() - _processStartTimeStamp);
|
||||
_processTimeStats.record(NanoTime.since(_processStartNanoTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.thread.Invocable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -1156,7 +1157,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ
|
|||
|
||||
protected class HttpStreamOverHTTP1 implements HttpStream
|
||||
{
|
||||
private final long _nanoTimestamp = System.nanoTime();
|
||||
private final long _nanoTime = NanoTime.now();
|
||||
private final long _id;
|
||||
private final String _method;
|
||||
private final HttpURI.Mutable _uri;
|
||||
|
@ -1385,7 +1386,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ
|
|||
@Override
|
||||
public long getNanoTimeStamp()
|
||||
{
|
||||
return _nanoTimestamp;
|
||||
return _nanoTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.server.internal.HttpChannelState;
|
|||
import org.eclipse.jetty.util.Blocker;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
|
@ -62,10 +63,10 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
{
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(ConnectorTimeoutTest.class);
|
||||
|
||||
protected static final int MAX_IDLE_TIME = 2000;
|
||||
private int sleepTime = MAX_IDLE_TIME + MAX_IDLE_TIME / 5;
|
||||
private int minimumTestRuntime = MAX_IDLE_TIME - MAX_IDLE_TIME / 5;
|
||||
private int maximumTestRuntime = MAX_IDLE_TIME * 10;
|
||||
protected static final long MAX_IDLE_TIME = 2000;
|
||||
private final long sleepTime = MAX_IDLE_TIME + MAX_IDLE_TIME / 5;
|
||||
private final long minimumTestRuntime = MAX_IDLE_TIME - MAX_IDLE_TIME / 5;
|
||||
private final long maximumTestRuntime = MAX_IDLE_TIME * 10;
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
|
@ -92,7 +93,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
OutputStream os = client.getOutputStream();
|
||||
InputStream is = client.getInputStream();
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
|
||||
assertTimeoutPreemptively(ofSeconds(10), () ->
|
||||
{
|
||||
|
@ -109,8 +110,9 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
assertEquals(-1, is.read());
|
||||
});
|
||||
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start > minimumTestRuntime);
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start < maximumTestRuntime);
|
||||
long elapsedMs = NanoTime.millisSince(start);
|
||||
assertTrue(elapsedMs > minimumTestRuntime);
|
||||
assertTrue(elapsedMs < maximumTestRuntime);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -125,7 +127,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
OutputStream os = client.getOutputStream();
|
||||
InputStream is = client.getInputStream();
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
|
||||
assertTimeoutPreemptively(ofSeconds(10), () ->
|
||||
{
|
||||
|
@ -146,8 +148,9 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
assertEquals(-1, is.read());
|
||||
});
|
||||
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start > minimumTestRuntime);
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start < maximumTestRuntime);
|
||||
long elapsedMs = NanoTime.millisSince(start);
|
||||
assertTrue(elapsedMs > minimumTestRuntime);
|
||||
assertTrue(elapsedMs < maximumTestRuntime);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -281,7 +284,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
InputStream is = client.getInputStream();
|
||||
assertFalse(client.isClosed());
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
|
||||
OutputStream os = client.getOutputStream();
|
||||
os.write(("GET / HTTP/1.1\r\n" +
|
||||
|
@ -319,7 +322,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start;
|
||||
long duration = NanoTime.millisSince(start);
|
||||
assertThat(duration, greaterThan(500L));
|
||||
|
||||
assertTimeoutPreemptively(ofSeconds(10), () ->
|
||||
|
@ -344,7 +347,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
|
||||
OutputStream os = client.getOutputStream();
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
os.write(("GET / HTTP/1.1\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
"Transfer-Encoding: chunked\r\n" +
|
||||
|
@ -377,7 +380,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
os.flush();
|
||||
}
|
||||
|
||||
long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start;
|
||||
long duration = NanoTime.millisSince(start);
|
||||
assertThat(duration, greaterThan(500L));
|
||||
|
||||
// read the response
|
||||
|
@ -422,7 +425,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
if (i % 1028 == 0)
|
||||
{
|
||||
Thread.sleep(20);
|
||||
// System.err.println("read "+TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||
}
|
||||
line = is.readLine();
|
||||
assertThat(line, notNullValue());
|
||||
|
@ -460,7 +462,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
line = is.readLine();
|
||||
}
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
try (StacklessLogging stackless = new StacklessLogging(HttpChannelState.class, AbstractConnection.class))
|
||||
{
|
||||
for (int i = 0; i < (128 * 1024); i++)
|
||||
|
@ -468,16 +470,13 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
if (i % 1028 == 0)
|
||||
{
|
||||
Thread.sleep(20);
|
||||
// System.err.println("read "+TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||
}
|
||||
line = is.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
long end = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long duration = end - start;
|
||||
assertThat(duration, lessThan(20L * 128L));
|
||||
assertThat(NanoTime.millisSince(start), lessThan(20L * 128L));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -490,7 +489,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
assertFalse(client.isClosed());
|
||||
|
||||
OutputStream os = client.getOutputStream();
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
os.write("GET ".getBytes("utf-8"));
|
||||
os.flush();
|
||||
|
||||
|
@ -508,14 +507,14 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
LOG.warn(e.getMessage());
|
||||
}
|
||||
});
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start < maximumTestRuntime);
|
||||
assertTrue(NanoTime.millisSince(start) < maximumTestRuntime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxIdleNothingSent() throws Exception
|
||||
{
|
||||
startServer(new EchoHandler());
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort());
|
||||
client.setSoTimeout(10000);
|
||||
InputStream is = client.getInputStream();
|
||||
|
@ -539,7 +538,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
LOG.warn("Unable to read stream", e);
|
||||
}
|
||||
});
|
||||
assertTrue(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start < maximumTestRuntime);
|
||||
assertTrue(NanoTime.millisSince(start) < maximumTestRuntime);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -552,7 +551,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
assertFalse(client.isClosed());
|
||||
|
||||
OutputStream os = client.getOutputStream();
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
|
@ -576,7 +575,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
int duration = (int)(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start);
|
||||
long duration = NanoTime.millisSince(start);
|
||||
assertThat(duration, greaterThanOrEqualTo(MAX_IDLE_TIME));
|
||||
assertThat(duration, lessThan(maximumTestRuntime));
|
||||
}
|
||||
|
@ -591,7 +590,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
assertFalse(client.isClosed());
|
||||
|
||||
OutputStream os = client.getOutputStream();
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"host: localhost:" + _serverURI.getPort() + "\r\n" +
|
||||
|
@ -616,7 +615,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
int duration = (int)(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start);
|
||||
long duration = NanoTime.millisSince(start);
|
||||
assertThat(duration + 100, greaterThanOrEqualTo(MAX_IDLE_TIME));
|
||||
assertThat(duration - 100, lessThan(maximumTestRuntime));
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import java.net.Socket;
|
|||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
|
@ -29,6 +28,7 @@ import org.eclipse.jetty.server.internal.HttpChannelState;
|
|||
import org.eclipse.jetty.server.internal.HttpConnection;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -77,7 +77,7 @@ public class ExtendedServerTest extends HttpServerTestBase
|
|||
@Override
|
||||
public Runnable onSelected()
|
||||
{
|
||||
_lastSelected = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
_lastSelected = NanoTime.now();
|
||||
return super.onSelected();
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ public class ExtendedServerTest extends HttpServerTestBase
|
|||
{
|
||||
OutputStream os = client.getOutputStream();
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
os.write("GET / HTTP/1.0\r\n".getBytes(StandardCharsets.ISO_8859_1));
|
||||
os.flush();
|
||||
Thread.sleep(200);
|
||||
|
@ -127,7 +127,7 @@ public class ExtendedServerTest extends HttpServerTestBase
|
|||
|
||||
// Read the response.
|
||||
String response = readResponse(client);
|
||||
long end = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long end = NanoTime.now();
|
||||
|
||||
assertThat(response, Matchers.containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, Matchers.containsString("DispatchedAt="));
|
||||
|
@ -136,8 +136,8 @@ public class ExtendedServerTest extends HttpServerTestBase
|
|||
s = s.substring(0, s.indexOf('\n'));
|
||||
long dispatched = Long.parseLong(s);
|
||||
|
||||
assertThat(dispatched, Matchers.greaterThanOrEqualTo(start));
|
||||
assertThat(dispatched, Matchers.lessThanOrEqualTo(end));
|
||||
assertThat(NanoTime.elapsed(start, dispatched), Matchers.greaterThanOrEqualTo(0L));
|
||||
assertThat(NanoTime.elapsed(dispatched, end), Matchers.greaterThanOrEqualTo(0L));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.util.Blocker;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.FuturePromise;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.component.Graceful;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -45,6 +46,7 @@ import static org.hamcrest.Matchers.greaterThan;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Disabled // TODO
|
||||
|
@ -148,6 +150,7 @@ public class GracefulStopTest
|
|||
assertTrue(handler.latch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(client.getInputStream());
|
||||
assertNotNull(response);
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getContent(), is("read [10/10]"));
|
||||
assertThat(response.get(HttpHeader.CONNECTION), nullValue());
|
||||
|
@ -163,6 +166,7 @@ public class GracefulStopTest
|
|||
assertTrue(handler.latch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(client.getInputStream());
|
||||
assertNotNull(response);
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getContent(), is("read [10/10]"));
|
||||
assertThat(response.get(HttpHeader.CONNECTION), nullValue());
|
||||
|
@ -171,21 +175,21 @@ public class GracefulStopTest
|
|||
Future<Integer> backgroundUnavailable(Socket client, byte[] post, ContextHandler context, TestHandler handler) throws Exception
|
||||
{
|
||||
FuturePromise<Integer> future = new FuturePromise<>();
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
while (context.isAvailable())
|
||||
{
|
||||
assertThat(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), lessThan(5000L));
|
||||
assertThat(NanoTime.millisSince(start), lessThan(5000L));
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
client.getOutputStream().write(concat(post, BODY_67890));
|
||||
client.getOutputStream().flush();
|
||||
HttpTester.Response response = HttpTester.parseResponse(client.getInputStream());
|
||||
|
||||
assertNotNull(response);
|
||||
future.succeeded(response.getStatus());
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -199,19 +203,16 @@ public class GracefulStopTest
|
|||
|
||||
void assertQuickStop() throws Exception
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
server.stop();
|
||||
long stop = System.nanoTime();
|
||||
long duration = TimeUnit.NANOSECONDS.toMillis(stop - start);
|
||||
assertThat(duration, lessThan(2000L));
|
||||
assertThat(NanoTime.millisSince(start), lessThan(2000L));
|
||||
}
|
||||
|
||||
void assertGracefulStop(LifeCycle lifecycle) throws Exception
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
lifecycle.stop();
|
||||
long stop = System.nanoTime();
|
||||
long duration = TimeUnit.NANOSECONDS.toMillis(stop - start);
|
||||
long duration = NanoTime.millisSince(start);
|
||||
assertThat(duration, greaterThan(50L));
|
||||
assertThat(duration, lessThan(5000L));
|
||||
}
|
||||
|
@ -219,6 +220,7 @@ public class GracefulStopTest
|
|||
void assertResponse(Socket client, boolean close) throws Exception
|
||||
{
|
||||
HttpTester.Response response = HttpTester.parseResponse(client.getInputStream());
|
||||
assertNotNull(response);
|
||||
assertThat(response.getStatus(), is(200));
|
||||
if (close)
|
||||
assertThat(response.get(HttpHeader.CONNECTION), is("close"));
|
||||
|
@ -239,11 +241,9 @@ public class GracefulStopTest
|
|||
|
||||
void assertQuickClose(Socket client) throws Exception
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
assertThat(client.getInputStream().read(), is(-1));
|
||||
long stop = System.nanoTime();
|
||||
long duration = TimeUnit.NANOSECONDS.toMillis(stop - start);
|
||||
assertThat(duration, lessThan(2000L));
|
||||
assertThat(NanoTime.millisSince(start), lessThan(2000L));
|
||||
}
|
||||
|
||||
void assertHandled(TestHandler handler, boolean error)
|
||||
|
@ -257,14 +257,13 @@ public class GracefulStopTest
|
|||
|
||||
void backgroundComplete(Socket client, TestHandler handler) throws Exception
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.latch.await();
|
||||
long now = System.nanoTime();
|
||||
Thread.sleep(100 - TimeUnit.NANOSECONDS.toMillis(now - start));
|
||||
Thread.sleep(100 - NanoTime.millisSince(start));
|
||||
client.getOutputStream().write(BODY_67890);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -15,12 +15,10 @@ package org.eclipse.jetty.server;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -30,7 +28,6 @@ import org.junit.jupiter.api.Disabled;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -287,15 +284,14 @@ public class HttpChannelEventTest
|
|||
@Override
|
||||
public void onRequestBegin(Request request)
|
||||
{
|
||||
request.setAttribute(attribute, System.nanoTime());
|
||||
request.setAttribute(attribute, NanoTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Request request)
|
||||
{
|
||||
long endTime = System.nanoTime();
|
||||
long beginTime = (Long)request.getAttribute(attribute);
|
||||
elapsed.set(endTime - beginTime);
|
||||
elapsed.set(NanoTime.since(beginTime));
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.eclipse.jetty.server.internal.HttpChannelState;
|
|||
import org.eclipse.jetty.server.internal.HttpConnection;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -933,9 +934,9 @@ public class HttpConnectionTest
|
|||
"5;\r\n" +
|
||||
"12345\r\n";
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
String response = _connector.getResponse(requests, 2000, TimeUnit.MILLISECONDS);
|
||||
assertThat(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start, lessThanOrEqualTo(2000L));
|
||||
assertThat(NanoTime.millisSince(start), lessThanOrEqualTo(2000L));
|
||||
|
||||
offset = checkContains(response, offset, "HTTP/1.1 200");
|
||||
offset = checkContains(response, offset, "pathInContext=/R1");
|
||||
|
|
|
@ -29,12 +29,13 @@ import org.eclipse.jetty.io.Connection;
|
|||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
public class MockHttpStream implements HttpStream
|
||||
{
|
||||
private static final Throwable SUCCEEDED = new Throwable();
|
||||
private static final Content.Chunk DEMAND = Content.Chunk.from(BufferUtil.EMPTY_BUFFER, false);
|
||||
private final long nano = System.nanoTime();
|
||||
private final long _nanoTime = NanoTime.now();
|
||||
private final AtomicReference<Content.Chunk> _content = new AtomicReference<>();
|
||||
private final AtomicReference<Throwable> _complete = new AtomicReference<>();
|
||||
private final CountDownLatch _completed = new CountDownLatch(1);
|
||||
|
@ -128,7 +129,7 @@ public class MockHttpStream implements HttpStream
|
|||
@Override
|
||||
public long getNanoTimeStamp()
|
||||
{
|
||||
return nano;
|
||||
return _nanoTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
|
|||
import org.eclipse.jetty.server.handler.HelloHandler;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -424,7 +425,7 @@ public class NotAcceptingTest
|
|||
|
||||
public static <T> void waitFor(Supplier<T> value, Matcher<T> matcher, long wait, TimeUnit units)
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -435,7 +436,7 @@ public class NotAcceptingTest
|
|||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
if ((System.nanoTime() - start) > units.toNanos(wait))
|
||||
if (NanoTime.since(start) > units.toNanos(wait))
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.io.ManagedSelector;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
|
@ -77,7 +78,7 @@ public class ServerConnectorAcceptTest
|
|||
{
|
||||
assertTrue(awaitBarrier(barrier));
|
||||
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
{
|
||||
try (Socket socket = new Socket("localhost", connector.getLocalPort()))
|
||||
|
@ -100,12 +101,11 @@ public class ServerConnectorAcceptTest
|
|||
latch.countDown();
|
||||
}
|
||||
}
|
||||
long elapsed = System.nanoTime() - start;
|
||||
System.err.printf("%d acceptors, %d threads, %d requests each, time = %d ms%n",
|
||||
acceptors,
|
||||
threads,
|
||||
iterations,
|
||||
TimeUnit.NANOSECONDS.toMillis(elapsed));
|
||||
NanoTime.millisSince(start));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -116,13 +116,12 @@ public class ServerConnectorTimeoutTest extends ConnectorTimeoutTest
|
|||
{
|
||||
try (Socket socket = new Socket((String)null, _connector.getLocalPort()))
|
||||
{
|
||||
socket.setSoTimeout(10 * MAX_IDLE_TIME);
|
||||
socket.setSoTimeout((int)(10 * MAX_IDLE_TIME));
|
||||
socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
|
||||
InputStream inputStream = socket.getInputStream();
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long start = NanoTime.now();
|
||||
String response = IO.toString(inputStream);
|
||||
long timeElapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start;
|
||||
assertThat(timeElapsed, greaterThanOrEqualTo(MAX_IDLE_TIME - 100L));
|
||||
assertThat(NanoTime.millisSince(start), greaterThanOrEqualTo(MAX_IDLE_TIME - 100L));
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -155,12 +156,12 @@ public class StopTest
|
|||
@Override
|
||||
public void close()
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(closeWait - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
|
||||
Thread.sleep(closeWait - NanoTime.millisSince(start));
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
|
@ -205,7 +206,7 @@ public class StopTest
|
|||
break;
|
||||
}
|
||||
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
try
|
||||
{
|
||||
server.stop();
|
||||
|
@ -215,10 +216,9 @@ public class StopTest
|
|||
{
|
||||
assertTrue(stopTimeout > 0 && stopTimeout < closeWait);
|
||||
}
|
||||
long stop = System.nanoTime();
|
||||
|
||||
// Check stop time was correct
|
||||
assertThat(TimeUnit.NANOSECONDS.toMillis(stop - start), stopTimeMatcher);
|
||||
assertThat(NanoTime.millisSince(start), stopTimeMatcher);
|
||||
|
||||
// Connection closed
|
||||
while (true)
|
||||
|
@ -226,7 +226,7 @@ public class StopTest
|
|||
int r = client.getInputStream().read();
|
||||
if (r == -1)
|
||||
break;
|
||||
assertThat(TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start), lessThan(10L));
|
||||
assertThat(NanoTime.millisSince(start), lessThan(10L));
|
||||
}
|
||||
|
||||
// onClose Thread interrupted or completed
|
||||
|
@ -346,10 +346,10 @@ public class StopTest
|
|||
}
|
||||
}).start();
|
||||
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
while (!connector.isShutdown())
|
||||
{
|
||||
assertThat(TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start), lessThan(10L));
|
||||
assertThat(NanoTime.secondsSince(start), lessThan(10L));
|
||||
Thread.sleep(10);
|
||||
}
|
||||
|
||||
|
|
|
@ -371,8 +371,8 @@ public class SSLEngineTest
|
|||
{
|
||||
// System.err.println("HANDLE "+request.getRequestURI());
|
||||
SecureRequestCustomizer.SslSessionData sslData = (SecureRequestCustomizer.SslSessionData)
|
||||
request.getAttribute("org.eclipse.jetty.servlet.request.ssl_session_data");
|
||||
String sslId = sslData.getId();
|
||||
request.getAttribute(SecureRequestCustomizer.DEFAULT_SSL_SESSION_DATA_ATTRIBUTE);
|
||||
String sslId = sslData.sessionId();
|
||||
assertNotNull(sslId);
|
||||
|
||||
Fields fields = Request.extractQueryParameters(request);
|
||||
|
|
|
@ -117,7 +117,6 @@ public class SSLSelectChannelConnectorLoadTest
|
|||
tasks[i] = threadPool.submit(workers[i]);
|
||||
}
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
while (true)
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
|
@ -126,13 +125,9 @@ public class SSLSelectChannelConnectorLoadTest
|
|||
{
|
||||
done &= task.isDone();
|
||||
}
|
||||
//System.err.print("\rIterations: " + Worker.totalIterations.get() + "/" + clients * iterations);
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
long end = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
//System.err.println();
|
||||
//System.err.println("Elapsed time: " + TimeUnit.MILLISECONDS.toSeconds(end - start) + "s");
|
||||
|
||||
for (Worker worker : workers)
|
||||
{
|
||||
|
@ -169,7 +164,6 @@ public class SSLSelectChannelConnectorLoadTest
|
|||
tasks[i] = threadPool.submit(workers[i]);
|
||||
}
|
||||
|
||||
long start = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
while (true)
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
|
@ -178,13 +172,9 @@ public class SSLSelectChannelConnectorLoadTest
|
|||
{
|
||||
done &= task.isDone();
|
||||
}
|
||||
// System.err.print("\rIterations: " + Worker.totalIterations.get() + "/" + clients * iterations);
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
long end = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
// System.err.println();
|
||||
// System.err.println("Elapsed time: " + TimeUnit.MILLISECONDS.toSeconds(end - start) + "s");
|
||||
|
||||
threadPool.shutdown();
|
||||
|
||||
|
|
|
@ -217,15 +217,15 @@ public class ServerConnectorSslServerTest extends HttpServerTestBase
|
|||
StringBuilder out = new StringBuilder();
|
||||
SSLSession session = (SSLSession)request.getAttribute("SSL_SESSION");
|
||||
|
||||
SecureRequestCustomizer.SslSessionData data = (SecureRequestCustomizer.SslSessionData)request.getAttribute("SSL_SESSION_data");
|
||||
SecureRequestCustomizer.SslSessionData data = (SecureRequestCustomizer.SslSessionData)request.getAttribute("SSL_SESSIONData");
|
||||
|
||||
out.append("Hello world").append('\n');
|
||||
out.append("scheme='").append(request.getHttpURI().getScheme()).append("'").append('\n');
|
||||
out.append("isSecure='").append(request.isSecure()).append("'").append('\n');
|
||||
out.append("X509Certificate='").append(data == null ? "" : data.getX509Certificates()).append("'").append('\n');
|
||||
out.append("X509Certificate='").append(data == null ? "" : data.peerCertificates()).append("'").append('\n');
|
||||
out.append("cipher_suite='").append(session == null ? "" : session.getCipherSuite()).append("'").append('\n');
|
||||
out.append("key_size='").append(data == null ? "" : data.getKeySize()).append("'").append('\n');
|
||||
out.append("ssl_session_id='").append(data == null ? "" : data.getId()).append("'").append('\n');
|
||||
out.append("key_size='").append(data == null ? "" : data.keySize()).append("'").append('\n');
|
||||
out.append("ssl_session_id='").append(data == null ? "" : data.sessionId()).append("'").append('\n');
|
||||
out.append("ssl_session='").append(session).append("'").append('\n');
|
||||
Content.Sink.write(response, true, out.toString(), callback);
|
||||
}
|
||||
|
|
|
@ -268,7 +268,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
|||
String longType = _dbAdaptor.getLongType();
|
||||
String stringType = _dbAdaptor.getStringType();
|
||||
|
||||
return "create table " + _tableName + " (" + _idColumn + " " + stringType + "(120), " +
|
||||
return "create table " + getSchemaTableName() + " (" + _idColumn + " " + stringType + "(120), " +
|
||||
_contextPathColumn + " " + stringType + "(60), " + _virtualHostColumn + " " + stringType + "(60), " + _lastNodeColumn + " " + stringType + "(60), " + _accessTimeColumn + " " + longType + ", " +
|
||||
_lastAccessTimeColumn + " " + longType + ", " + _createTimeColumn + " " + longType + ", " + _cookieTimeColumn + " " + longType + ", " +
|
||||
_lastSavedTimeColumn + " " + longType + ", " + _expiryTimeColumn + " " + longType + ", " + _maxIntervalColumn + " " + longType + ", " +
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
package org.eclipse.jetty.util.ajax;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -40,7 +40,7 @@ public class JSONPojoConvertorFactoryTest
|
|||
jsonIn.addConvertor(Enum.class, new JSONEnumConvertor());
|
||||
|
||||
Foo foo = new Foo();
|
||||
foo.setName("Foo @ " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||
foo.setName("Foo @ " + NanoTime.now());
|
||||
foo.setInt1(1);
|
||||
foo.setInt2(2);
|
||||
foo.setLong1(1000001L);
|
||||
|
@ -83,7 +83,7 @@ public class JSONPojoConvertorFactoryTest
|
|||
jsonIn.addConvertor(Enum.class, new JSONEnumConvertor());
|
||||
|
||||
Foo foo = new Foo();
|
||||
foo.setName("Foo @ " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||
foo.setName("Foo @ " + NanoTime.now());
|
||||
foo.setInt1(1);
|
||||
foo.setInt2(2);
|
||||
foo.setLong1(1000001L);
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.util.ajax;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -36,7 +35,7 @@ public class JSONPojoConvertorTest
|
|||
json.addConvertor(Baz.class, new JSONPojoConvertor(Baz.class));
|
||||
|
||||
Foo foo = new Foo();
|
||||
foo.setName("Foo @ " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||
foo.setName("Foo @ " + NanoTime.now());
|
||||
foo.setInt1(1);
|
||||
foo.setInt2(2);
|
||||
foo.setLong1(1000001L);
|
||||
|
@ -76,7 +75,7 @@ public class JSONPojoConvertorTest
|
|||
json.addConvertor(Baz.class, new JSONPojoConvertor(Baz.class, new String[]{"boolean2"}));
|
||||
|
||||
Foo foo = new Foo();
|
||||
foo.setName("Foo @ " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||
foo.setName("Foo @ " + NanoTime.now());
|
||||
foo.setInt1(1);
|
||||
foo.setInt2(2);
|
||||
foo.setLong1(1000001L);
|
||||
|
|
|
@ -1071,16 +1071,18 @@ public class BufferUtil
|
|||
|
||||
public static ByteBuffer toMappedBuffer(Resource resource) throws IOException
|
||||
{
|
||||
if (!resource.isMemoryMappable())
|
||||
Path path = resource.getPath();
|
||||
if (path == null || !path.toUri().getScheme().endsWith("file"))
|
||||
return null;
|
||||
return toMappedBuffer(resource.getPath());
|
||||
return toMappedBuffer(path);
|
||||
}
|
||||
|
||||
public static ByteBuffer toMappedBuffer(Resource resource, long pos, long len) throws IOException
|
||||
{
|
||||
if (!resource.isMemoryMappable())
|
||||
Path path = resource.getPath();
|
||||
if (path == null || !path.toUri().getScheme().endsWith("file"))
|
||||
return null;
|
||||
return toMappedBuffer(resource.getPath(), pos, len);
|
||||
return toMappedBuffer(path, pos, len);
|
||||
}
|
||||
|
||||
public static String toSummaryString(ByteBuffer buffer)
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.util;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>Utility class with methods that deal with {@link System#nanoTime()}.</p>
|
||||
*/
|
||||
public class NanoTime
|
||||
{
|
||||
/**
|
||||
* @return the current nanoTime via {@link System#nanoTime()}
|
||||
*/
|
||||
public static long now()
|
||||
{
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the nanoseconds elapsed between two nanoTimes.</p>
|
||||
*
|
||||
* @param beginNanoTime the begin nanoTime
|
||||
* @param endNanoTime the end nanoTime
|
||||
* @return the nanoseconds elapsed
|
||||
*/
|
||||
public static long elapsed(long beginNanoTime, long endNanoTime)
|
||||
{
|
||||
return endNanoTime - beginNanoTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the nanoseconds elapsed since a begin nanoTime and the current nanoTime.</p>
|
||||
*
|
||||
* @param beginNanoTime the begin nanoTime
|
||||
* @return the nanoseconds elapsed since the given begin nanoTime and the current nanoTime
|
||||
*/
|
||||
public static long since(long beginNanoTime)
|
||||
{
|
||||
return elapsed(beginNanoTime, now());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the nanoseconds remaining from the current nanoTime until an end nanoTime.</p>
|
||||
*
|
||||
* @param endNanoTime the end nanoTime
|
||||
* @return the nanoseconds remaining from the current nanoTime until the given end nanoTime
|
||||
*/
|
||||
public static long until(long endNanoTime)
|
||||
{
|
||||
return elapsed(now(), endNanoTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the milliseconds elapsed between two nanoTimes.</p>
|
||||
*
|
||||
* @param beginNanoTime the begin nanoTime
|
||||
* @param endNanoTime the end nanoTime
|
||||
* @return the milliseconds elapsed
|
||||
*/
|
||||
public static long millisElapsed(long beginNanoTime, long endNanoTime)
|
||||
{
|
||||
return TimeUnit.NANOSECONDS.toMillis(elapsed(beginNanoTime, endNanoTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the milliseconds elapsed between a begin nanoTime and the current nanoTime.</p>
|
||||
*
|
||||
* @param beginNanoTime the begin nanoTime
|
||||
* @return the milliseconds elapsed between the given begin nanoTime and the current nanoTime
|
||||
*/
|
||||
public static long millisSince(long beginNanoTime)
|
||||
{
|
||||
return millisElapsed(beginNanoTime, now());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the milliseconds remaining between the current nanoTime and an end nanoTime.</p>
|
||||
*
|
||||
* @param endNanoTime the end nanoTime
|
||||
* @return the milliseconds remaining between the current nanoTime and the given end nanoTime
|
||||
*/
|
||||
public static long millisUntil(long endNanoTime)
|
||||
{
|
||||
return millisElapsed(now(), endNanoTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the seconds elapsed between two nanoTimes.</p>
|
||||
*
|
||||
* @param beginNanoTime the begin nanoTime
|
||||
* @param endNanoTime the end nanoTime
|
||||
* @return the seconds elapsed
|
||||
*/
|
||||
public static long secondsElapsed(long beginNanoTime, long endNanoTime)
|
||||
{
|
||||
return TimeUnit.NANOSECONDS.toSeconds(elapsed(beginNanoTime, endNanoTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the seconds elapsed between a begin nanoTime and the current nanoTime.</p>
|
||||
*
|
||||
* @param beginNanoTime the begin nanoTime
|
||||
* @return the seconds elapsed between the given begin nanoTime and the current nanoTime
|
||||
*/
|
||||
public static long secondsSince(long beginNanoTime)
|
||||
{
|
||||
return secondsElapsed(beginNanoTime, now());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calculates the seconds remaining between the current nanoTime and an end nanoTime.</p>
|
||||
*
|
||||
* @param endNanoTime the end nanoTime
|
||||
* @return the seconds remaining between the current nanoTime and the given end nanoTime
|
||||
*/
|
||||
public static long secondsUntil(long endNanoTime)
|
||||
{
|
||||
return secondsElapsed(now(), endNanoTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns whether the first nanoTime is strictly before the second nanoTime.</p>
|
||||
* <p>Reads as: {@code "is nanoTime1 strictly before nanoTime2?"}.</p>
|
||||
* <p>Avoids the common mistake of comparing the 2 nanoTimes with the
|
||||
* less-than {@code <} operator, which cannot be used when comparing nanoTimes,
|
||||
* as specified in {@link System#nanoTime()}.</p>
|
||||
*
|
||||
* @param nanoTime1 the first nanoTime
|
||||
* @param nanoTime2 the second nanoTime
|
||||
* @return whether the first nanoTime is strictly before the second nanoTime
|
||||
* @see #isBeforeOrSame(long, long)
|
||||
*/
|
||||
public static boolean isBefore(long nanoTime1, long nanoTime2)
|
||||
{
|
||||
return nanoTime1 - nanoTime2 < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns whether the first nanoTime is before or the same as the second nanoTime.</p>
|
||||
*
|
||||
* @param nanoTime1 the first nanoTime
|
||||
* @param nanoTime2 the second nanoTime
|
||||
* @return whether the first nanoTime is before or the same as the second nanoTime
|
||||
* @see #isBefore(long, long)
|
||||
*/
|
||||
public static boolean isBeforeOrSame(long nanoTime1, long nanoTime2)
|
||||
{
|
||||
return nanoTime1 - nanoTime2 <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Spin waits for the specified number of nanoseconds.</p>
|
||||
*
|
||||
* @param nanos the amount of nanoseconds to spin wait
|
||||
*/
|
||||
public static void spinWait(long nanos)
|
||||
{
|
||||
long start = now();
|
||||
while (since(start) < nanos)
|
||||
{
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
|
||||
private NanoTime()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -114,7 +114,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
|
||||
public void setPauseUntil(long time)
|
||||
{
|
||||
if (time > pauseUntil)
|
||||
if (NanoTime.isBefore(pauseUntil, time))
|
||||
pauseUntil = time;
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
{
|
||||
if (pauseUntil == 0)
|
||||
return false;
|
||||
if (pauseUntil > now)
|
||||
if (NanoTime.isBefore(now, pauseUntil))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("PAUSED {}", this);
|
||||
|
@ -494,32 +494,28 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
}
|
||||
}
|
||||
|
||||
public static enum DirAction
|
||||
public enum DirAction
|
||||
{
|
||||
IGNORE, WATCH, ENTER;
|
||||
IGNORE, WATCH, ENTER
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for path change events
|
||||
*/
|
||||
public static interface Listener extends EventListener
|
||||
public interface Listener extends EventListener
|
||||
{
|
||||
void onPathWatchEvent(PathWatchEvent event);
|
||||
}
|
||||
|
||||
/**
|
||||
* EventListListener
|
||||
*
|
||||
* Listener that reports accumulated events in one shot
|
||||
*/
|
||||
public static interface EventListListener extends EventListener
|
||||
public interface EventListListener extends EventListener
|
||||
{
|
||||
void onPathWatchEvents(List<PathWatchEvent> events);
|
||||
}
|
||||
|
||||
/**
|
||||
* PathWatchEvent
|
||||
*
|
||||
* Represents a file event. Reported to registered listeners.
|
||||
*/
|
||||
public class PathWatchEvent
|
||||
|
@ -536,7 +532,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
this.path = path;
|
||||
this.type = type;
|
||||
this.config = config;
|
||||
checked = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
checked = NanoTime.now();
|
||||
check();
|
||||
}
|
||||
|
||||
|
@ -565,7 +561,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
this.type = PathWatchEventType.UNKNOWN;
|
||||
}
|
||||
this.config = config;
|
||||
checked = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
checked = NanoTime.now();
|
||||
check();
|
||||
}
|
||||
|
||||
|
@ -599,7 +595,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
check();
|
||||
|
||||
if (lastModified == modified && lastLength == length)
|
||||
return (now - checked) >= quietTime;
|
||||
return NanoTime.elapsed(checked, now) >= quietTime;
|
||||
|
||||
checked = now;
|
||||
return false;
|
||||
|
@ -607,7 +603,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
|
||||
public long toQuietCheck(long now, long quietTime)
|
||||
{
|
||||
long check = quietTime - (now - checked);
|
||||
long check = quietTime - NanoTime.elapsed(checked, now);
|
||||
if (check <= 0)
|
||||
return quietTime;
|
||||
return check;
|
||||
|
@ -615,10 +611,10 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
|
||||
public void modified()
|
||||
{
|
||||
long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long now = NanoTime.now();
|
||||
checked = now;
|
||||
check();
|
||||
config.setPauseUntil(now + getUpdateQuietTimeMillis());
|
||||
config.setPauseUntil(now + getUpdateQuietTimeNanos());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -683,13 +679,11 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
}
|
||||
|
||||
/**
|
||||
* PathWatchEventType
|
||||
*
|
||||
* Type of an event
|
||||
*/
|
||||
public static enum PathWatchEventType
|
||||
public enum PathWatchEventType
|
||||
{
|
||||
ADDED, DELETED, MODIFIED, UNKNOWN;
|
||||
ADDED, DELETED, MODIFIED, UNKNOWN
|
||||
}
|
||||
|
||||
private static final boolean IS_WINDOWS;
|
||||
|
@ -853,7 +847,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
this.watchService = FileSystems.getDefault().newWatchService();
|
||||
|
||||
//ensure setting of quiet time is appropriate now we have a watcher
|
||||
setUpdateQuietTime(getUpdateQuietTimeMillis(), TimeUnit.MILLISECONDS);
|
||||
setUpdateQuietTime(getUpdateQuietTimeNanos(), TimeUnit.NANOSECONDS);
|
||||
|
||||
// Register all watched paths, walking dir hierarchies as needed, possibly generating
|
||||
// fake add events if notifyExistingOnStart is true
|
||||
|
@ -922,14 +916,9 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
return listeners.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the quiet time.
|
||||
*
|
||||
* @return the quiet time in millis
|
||||
*/
|
||||
public long getUpdateQuietTimeMillis()
|
||||
long getUpdateQuietTimeNanos()
|
||||
{
|
||||
return TimeUnit.MILLISECONDS.convert(updateQuietTimeDuration, updateQuietTimeUnit);
|
||||
return TimeUnit.NANOSECONDS.convert(updateQuietTimeDuration, updateQuietTimeUnit);
|
||||
}
|
||||
|
||||
private void registerTree(Path dir, Config config, boolean notify) throws IOException
|
||||
|
@ -1039,8 +1028,6 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
* single MODIFY event. Both the accumulation of events and coalescing of MODIFY
|
||||
* events reduce the number and frequency of event reporting for "noisy" files (ie
|
||||
* those that are undergoing rapid change).
|
||||
*
|
||||
* @see java.lang.Runnable#run()
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
|
@ -1051,7 +1038,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
LOG.debug("Starting java.nio file watching with {}", watchService);
|
||||
}
|
||||
|
||||
long waitTime = getUpdateQuietTimeMillis();
|
||||
long waitTime = getUpdateQuietTimeNanos();
|
||||
|
||||
WatchService watch = watchService;
|
||||
|
||||
|
@ -1063,7 +1050,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
try
|
||||
{
|
||||
// Reset all keys before watching
|
||||
long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long now = NanoTime.now();
|
||||
for (Map.Entry<WatchKey, Config> e : keys.entrySet())
|
||||
{
|
||||
WatchKey k = e.getKey();
|
||||
|
@ -1081,7 +1068,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Waiting for poll({})", waitTime);
|
||||
key = waitTime < 0 ? watch.take() : waitTime > 0 ? watch.poll(waitTime, updateQuietTimeUnit) : watch.poll();
|
||||
key = waitTime < 0 ? watch.take() : waitTime > 0 ? watch.poll(waitTime, TimeUnit.NANOSECONDS) : watch.poll();
|
||||
|
||||
// handle all active keys
|
||||
while (key != null)
|
||||
|
@ -1218,7 +1205,7 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("processPending> {}", pending.values());
|
||||
|
||||
long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
long now = NanoTime.now();
|
||||
long wait = Long.MAX_VALUE;
|
||||
|
||||
// pending map is maintained in LRU order
|
||||
|
@ -1230,7 +1217,8 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
continue;
|
||||
|
||||
// if the path is quiet move to events
|
||||
if (event.isQuiet(now, getUpdateQuietTimeMillis()))
|
||||
long quietTime = getUpdateQuietTimeNanos();
|
||||
if (event.isQuiet(now, quietTime))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("isQuiet {}", event);
|
||||
|
@ -1239,11 +1227,11 @@ public class PathWatcher extends AbstractLifeCycle implements Runnable
|
|||
}
|
||||
else
|
||||
{
|
||||
long msToCheck = event.toQuietCheck(now, getUpdateQuietTimeMillis());
|
||||
long nsToCheck = event.toQuietCheck(now, quietTime);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("pending {} {}", event, msToCheck);
|
||||
if (msToCheck < wait)
|
||||
wait = msToCheck;
|
||||
LOG.debug("pending {} {}", event, nsToCheck);
|
||||
if (nsToCheck < wait)
|
||||
wait = nsToCheck;
|
||||
}
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
|
@ -129,7 +129,7 @@ public class Pool<T> implements AutoCloseable, Dumpable
|
|||
public Pool(StrategyType strategyType, int maxEntries, boolean cache, ToIntFunction<T> maxMultiplex)
|
||||
{
|
||||
this.maxEntries = maxEntries;
|
||||
this.strategyType = strategyType;
|
||||
this.strategyType = Objects.requireNonNull(strategyType);
|
||||
this.cache = cache ? new ThreadLocal<>() : null;
|
||||
this.nextIndex = strategyType == StrategyType.ROUND_ROBIN ? new AtomicInteger() : null;
|
||||
this.maxMultiplex = Objects.requireNonNull(maxMultiplex);
|
||||
|
@ -205,14 +205,25 @@ public class Pool<T> implements AutoCloseable, Dumpable
|
|||
try (AutoLock ignored = lock.lock())
|
||||
{
|
||||
if (closed)
|
||||
{
|
||||
if (LOGGER.isDebugEnabled())
|
||||
LOGGER.debug("{} is closed, returning null reserved entry", this);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we have no space
|
||||
if (maxEntries > 0 && entries.size() >= maxEntries)
|
||||
int entriesSize = entries.size();
|
||||
if (maxEntries > 0 && entriesSize >= maxEntries)
|
||||
{
|
||||
if (LOGGER.isDebugEnabled())
|
||||
LOGGER.debug("{} has no space: {} >= {}, returning null reserved entry", this, entriesSize, maxEntries);
|
||||
return null;
|
||||
}
|
||||
|
||||
Entry entry = new Entry();
|
||||
entries.add(entry);
|
||||
if (LOGGER.isDebugEnabled())
|
||||
LOGGER.debug("{} returning new reserved entry {}", this, entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
@ -368,6 +379,9 @@ public class Pool<T> implements AutoCloseable, Dumpable
|
|||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (LOGGER.isDebugEnabled())
|
||||
LOGGER.debug("Closing {}", this);
|
||||
|
||||
List<Entry> copy;
|
||||
try (AutoLock ignored = lock.lock())
|
||||
{
|
||||
|
|
|
@ -161,11 +161,10 @@ public interface SocketAddressResolver
|
|||
|
||||
try
|
||||
{
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
InetAddress[] addresses = InetAddress.getAllByName(host);
|
||||
long elapsed = System.nanoTime() - start;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
|
||||
LOG.debug("Resolved {} in {} ms", host, NanoTime.millisSince(start));
|
||||
|
||||
List<InetSocketAddress> result = new ArrayList<>(addresses.length);
|
||||
for (InetAddress address : addresses)
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.nio.file.Paths;
|
|||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.util.Index;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
@ -235,25 +236,6 @@ public class PathResource extends Resource
|
|||
return Files.exists(alias != null ? alias : path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(Resource resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (resource instanceof PathResource)
|
||||
{
|
||||
Path path = resource.getPath();
|
||||
return Files.isSameFile(getPath(), path);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ignored", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
|
@ -270,18 +252,7 @@ public class PathResource extends Resource
|
|||
return false;
|
||||
}
|
||||
PathResource other = (PathResource)obj;
|
||||
if (path == null)
|
||||
{
|
||||
if (other.path != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!path.equals(other.path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return Objects.equals(path, other.path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,12 +269,6 @@ public class PathResource extends Resource
|
|||
return path.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemoryMappable()
|
||||
{
|
||||
return "file".equalsIgnoreCase(uri.getScheme());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
|
@ -313,10 +278,7 @@ public class PathResource extends Resource
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((path == null) ? 0 : path.hashCode());
|
||||
return result;
|
||||
return Objects.hashCode(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.nio.file.attribute.FileTime;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.util.IO;
|
||||
|
@ -47,7 +48,7 @@ import org.slf4j.LoggerFactory;
|
|||
* Supports real filesystems, and also <a href="https://docs.oracle.com/en/java/javase/17/docs/api/jdk.zipfs/module-summary.html">ZipFS</a>.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class Resource
|
||||
public abstract class Resource implements Iterable<Resource>
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
|
||||
private static final LinkOption[] NO_FOLLOW_LINKS = new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
|
||||
|
@ -143,16 +144,18 @@ public abstract class Resource
|
|||
public abstract boolean isContainedIn(Resource r);
|
||||
|
||||
/**
|
||||
* Return true if the passed Resource represents the same resource as the Resource.
|
||||
* For many resource types, this is equivalent to {@link #equals(Object)}, however
|
||||
* for resources types that support aliasing, this maybe some other check (e.g. {@link java.nio.file.Files#isSameFile(Path, Path)}).
|
||||
* Return an Iterator of all Resource's referenced in this Resource.
|
||||
*
|
||||
* @param resource The resource to check
|
||||
* @return true if the passed resource represents the same resource.
|
||||
* <p>
|
||||
* This is meaningful if you have a Composite Resource, otherwise it will be a single entry Iterator.
|
||||
* </p>
|
||||
*
|
||||
* @return the iterator of Resources.
|
||||
*/
|
||||
public boolean isSame(Resource resource)
|
||||
@Override
|
||||
public Iterator<Resource> iterator()
|
||||
{
|
||||
return equals(resource);
|
||||
return List.of(this).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,16 +258,6 @@ public abstract class Resource
|
|||
return Files.newByteChannel(getPath(), StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the resource supports being loaded as a memory-mapped ByteBuffer.
|
||||
*
|
||||
* @return true if the resource supports memory-mapped ByteBuffer, false otherwise.
|
||||
*/
|
||||
public boolean isMemoryMappable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* list of resource names contained in the given resource.
|
||||
* Ordering is unspecified, so callers may wish to sort the return value to ensure deterministic behavior.
|
||||
|
|
|
@ -23,7 +23,9 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
@ -257,6 +259,12 @@ public class ResourceCollection extends Resource
|
|||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Resource> iterator()
|
||||
{
|
||||
return _resources.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The list of resource names(merged) contained in the collection of resources.
|
||||
*/
|
||||
|
@ -286,6 +294,23 @@ public class ResourceCollection extends Resource
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ResourceCollection other = (ResourceCollection)o;
|
||||
return Objects.equals(_resources, other._resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(_resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of resources
|
||||
*/
|
||||
|
|
|
@ -13,17 +13,18 @@
|
|||
|
||||
package org.eclipse.jetty.util.statistic;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
|
||||
/**
|
||||
* Counts the rate that {@link Long}s are added to this from the time of creation or the last call to {@link #reset()}.
|
||||
*/
|
||||
public class RateCounter
|
||||
{
|
||||
private final LongAdder _total = new LongAdder();
|
||||
private final AtomicLong _timeStamp = new AtomicLong(System.nanoTime());
|
||||
private final AtomicLong _nanoTime = new AtomicLong(NanoTime.now());
|
||||
|
||||
public void add(long l)
|
||||
{
|
||||
|
@ -32,13 +33,13 @@ public class RateCounter
|
|||
|
||||
public long getRate()
|
||||
{
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - _timeStamp.get());
|
||||
long elapsed = NanoTime.millisSince(_nanoTime.get());
|
||||
return elapsed == 0 ? 0 : _total.sum() * 1000 / elapsed;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
_timeStamp.getAndSet(System.nanoTime());
|
||||
_nanoTime.set(NanoTime.now());
|
||||
_total.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.Deque;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
|
||||
/**
|
||||
|
@ -69,14 +70,13 @@ public class RateStatistic
|
|||
|
||||
private void update()
|
||||
{
|
||||
update(System.nanoTime());
|
||||
update(NanoTime.now());
|
||||
}
|
||||
|
||||
private void update(long now)
|
||||
{
|
||||
long expire = now - _nanoPeriod;
|
||||
Long head = _samples.peekFirst();
|
||||
while (head != null && head < expire)
|
||||
while (head != null && NanoTime.elapsed(head, now) > _nanoPeriod)
|
||||
{
|
||||
_samples.removeFirst();
|
||||
head = _samples.peekFirst();
|
||||
|
@ -104,7 +104,7 @@ public class RateStatistic
|
|||
*/
|
||||
public int record()
|
||||
{
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
_count++;
|
||||
|
@ -151,7 +151,7 @@ public class RateStatistic
|
|||
Long head = _samples.peekFirst();
|
||||
if (head == null)
|
||||
return -1;
|
||||
return units.convert(System.nanoTime() - head, TimeUnit.NANOSECONDS);
|
||||
return units.convert(NanoTime.since(head), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,11 +173,11 @@ public class RateStatistic
|
|||
|
||||
public String dump(TimeUnit units)
|
||||
{
|
||||
long now = System.nanoTime();
|
||||
long now = NanoTime.now();
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
String samples = _samples.stream()
|
||||
.mapToLong(t -> units.convert(now - t, TimeUnit.NANOSECONDS))
|
||||
.mapToLong(t -> units.convert(NanoTime.elapsed(t, now), TimeUnit.NANOSECONDS))
|
||||
.mapToObj(Long::toString)
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
return String.format("%s%n%s", toString(now), samples);
|
||||
|
@ -187,7 +187,7 @@ public class RateStatistic
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return toString(System.nanoTime());
|
||||
return toString(NanoTime.now());
|
||||
}
|
||||
|
||||
private String toString(long nanoTime)
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.util.thread;
|
|||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
|
@ -56,24 +57,24 @@ public class MonitoredQueuedThreadPool extends QueuedThreadPool
|
|||
public void execute(final Runnable job)
|
||||
{
|
||||
queueStats.increment();
|
||||
long begin = System.nanoTime();
|
||||
long begin = NanoTime.now();
|
||||
super.execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
long queueLatency = System.nanoTime() - begin;
|
||||
long queueLatency = NanoTime.since(begin);
|
||||
queueStats.decrement();
|
||||
threadStats.increment();
|
||||
queueLatencyStats.record(queueLatency);
|
||||
long start = System.nanoTime();
|
||||
long start = NanoTime.now();
|
||||
try
|
||||
{
|
||||
job.run();
|
||||
}
|
||||
finally
|
||||
{
|
||||
long taskLatency = System.nanoTime() - start;
|
||||
long taskLatency = NanoTime.since(start);
|
||||
threadStats.decrement();
|
||||
taskLatencyStats.record(taskLatency);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
|
||||
import org.eclipse.jetty.util.AtomicBiInteger;
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.VirtualThreads;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
|
@ -216,7 +217,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
}
|
||||
addBean(_tryExecutor);
|
||||
|
||||
_lastShrink.set(System.nanoTime());
|
||||
_lastShrink.set(NanoTime.now());
|
||||
|
||||
super.doStart();
|
||||
// The threads count set to MIN_VALUE is used to signal to Runners that the pool is stopped.
|
||||
|
@ -249,7 +250,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
break;
|
||||
|
||||
// try to let jobs complete naturally for half our stop time
|
||||
joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
|
||||
joinThreads(NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
|
||||
|
||||
// If we still have threads running, get a bit more aggressive
|
||||
|
||||
|
@ -264,7 +265,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
}
|
||||
|
||||
// wait again for the other half of our stop time
|
||||
joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
|
||||
joinThreads(NanoTime.now() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2);
|
||||
|
||||
Thread.yield();
|
||||
|
||||
|
@ -323,7 +324,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
if (thread == Thread.currentThread())
|
||||
continue;
|
||||
|
||||
long canWait = TimeUnit.NANOSECONDS.toMillis(stopByNanos - System.nanoTime());
|
||||
long canWait = NanoTime.millisUntil(stopByNanos);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Waiting for {} for {}", thread, canWait);
|
||||
if (canWait <= 0)
|
||||
|
@ -826,7 +827,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Starting {}", thread);
|
||||
_threads.add(thread);
|
||||
_lastShrink.set(System.nanoTime());
|
||||
_lastShrink.set(NanoTime.now());
|
||||
thread.start();
|
||||
started = true;
|
||||
}
|
||||
|
@ -1050,8 +1051,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
if (idleTimeout > 0 && getThreads() > _minThreads)
|
||||
{
|
||||
long last = _lastShrink.get();
|
||||
long now = System.nanoTime();
|
||||
if ((now - last) > TimeUnit.MILLISECONDS.toNanos(idleTimeout) && _lastShrink.compareAndSet(last, now))
|
||||
long now = NanoTime.now();
|
||||
if (NanoTime.millisElapsed(last, now) > idleTimeout && _lastShrink.compareAndSet(last, now))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("shrinking {}", QueuedThreadPool.this);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue