Merge remote-tracking branch 'eclipse/jetty-10.0.x' into jetty-10.0.x-3170-websocket-proxy

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2019-06-11 15:17:27 +10:00
commit 327783e1ce
980 changed files with 25696 additions and 12963 deletions

1
.gitignore vendored
View File

@ -19,6 +19,7 @@ bin/
*.backup *.backup
*.debug *.debug
*.dump *.dump
.attach_pid*
# vim # vim
.*.sw[a-p] .*.sw[a-p]

41
Jenkinsfile vendored
View File

@ -40,16 +40,6 @@ pipeline {
} }
} }
stage("Build / Test - JDK11") {
agent { node { label 'linux' } }
options { timeout(time: 120, unit: 'MINUTES') }
steps {
mavenBuild("jdk11", "-Pmongodb install", "maven3", false)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
maven_invoker reportsFilenamePattern: "**/target/invoker-reports/BUILD*.xml", invokerBuildDir: "**/target/it"
}
}
stage("Build / Test - JDK12") { stage("Build / Test - JDK12") {
agent { node { label 'linux' } } agent { node { label 'linux' } }
options { timeout(time: 120, unit: 'MINUTES') } options { timeout(time: 120, unit: 'MINUTES') }
@ -71,8 +61,37 @@ pipeline {
} }
} }
} }
/*
post {
failure {
slackNotif()
}
unstable {
slackNotif()
}
fixed {
slackNotif()
}
}
*/
} }
/*
def slackNotif() {
script {
if (env.BRANCH_NAME=='jetty-10.0.x' ||
env.BRANCH_NAME=='jetty-9.4.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}"
}
}
}
*/
/** /**
* To other developers, if you are using this method above, please use the following syntax. * To other developers, if you are using this method above, please use the following syntax.
* *
@ -97,7 +116,7 @@ def mavenBuild(jdk, cmdline, mvnName, junitPublishDisabled) {
mavenOpts: mavenOpts, mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) { mavenLocalRepo: localRepo) {
// Some common Maven command line + provided command line // Some common Maven command line + provided command line
sh "mvn -V -B -T3 -e -fae -Dmaven.test.failure.ignore=true -Djetty.testtracker.log=true $cmdline -Dunix.socket.tmp=" + env.JENKINS_HOME sh "mvn -Pci -V -B -T3 -e -fae -Dmaven.test.failure.ignore=true -Djetty.testtracker.log=true $cmdline -Dunix.socket.tmp=" + env.JENKINS_HOME
} }
} }

View File

@ -2,53 +2,49 @@
def branch = params.get("JETTY_BRANCH" ,"jetty-10.0.x") def branch = params.get("JETTY_BRANCH" ,"jetty-10.0.x")
def owner = params.get("REPO_OWNER", "eclipse") def owner = params.get("REPO_OWNER", "eclipse")
def jdk = params.get("JDK", "jdk11")
def jmhJarPath = params.get("jmhJarPath","tests/jetty-jmh/target/benchmarks.jar")
currentBuild.description = "Build branch $branch with jdk $jdk"
node("linux") { node("linux") {
// System Dependent Locations // System Dependent Locations
def mvntool = tool name: 'maven3.5', type: 'hudson.tasks.Maven$MavenInstallation'
def jdktool = tool name: "jdk8", type: 'hudson.model.JDK'
def mvnName = 'maven3.5' def mvnName = 'maven3.5'
def localRepo = "${env.JENKINS_HOME}/${env.EXECUTOR_NUMBER}" def localRepo = "${env.JENKINS_HOME}/${env.EXECUTOR_NUMBER}"
def settingsName = 'oss-settings.xml' def settingsName = 'oss-settings.xml'
def mavenOpts = '-Xms1g -Xmx4g -Djava.awt.headless=true' def mavenOpts = '-Xms1g -Xmx4g -Djava.awt.headless=true'
// Environment
List mvnEnv = ["PATH+MVN=${mvntool}/bin", "PATH+JDK=${jdktool}/bin", "JAVA_HOME=${jdktool}/", "MAVEN_HOME=${mvntool}"]
mvnEnv.add("MAVEN_OPTS=$mavenOpts")
stage("Checkout") { stage("Checkout") {
git url: "https://github.com/$owner/jetty.project.git", branch: "$branch" git url: "https://github.com/$owner/jetty.project.git", branch: "$branch"
} }
stage("Compile") { stage("Compile") {
withEnv(mvnEnv) { timeout(time: 15, unit: 'MINUTES') {
timeout(time: 15, unit: 'MINUTES') { withMaven(
withMaven( maven: mvnName,
maven: mvnName, jdk: jdk,
jdk: "jdk11", publisherStrategy: 'EXPLICIT',
publisherStrategy: 'EXPLICIT', globalMavenSettingsConfig: settingsName,
globalMavenSettingsConfig: settingsName, mavenOpts: mavenOpts,
mavenOpts: mavenOpts, mavenLocalRepo: localRepo) {
mavenLocalRepo: localRepo) { sh "mvn -V -B clean install -DskipTests -T6 -e -pl :jetty-jmh -am"
sh "mvn -V -B clean install -DskipTests -T6 -e"
}
} }
stash name: 'perf-tests', includes: 'jetty-jmh/target/benchmarks.jar'
} }
stash name: 'perf-tests', includes: jmhJarPath
} }
} }
// jmh run // jmh run
node( 'jmh-build-node' ) {
stage("jmh-run") { stage("jmh-run") {
node( 'jmh-build-node' ) { timeout( time: 180, unit: 'MINUTES' ) {
timeout( time: 120, unit: 'MINUTES' ) { withEnv( ["JAVA_HOME=${tool "$jdk"}"] ) {
withEnv( ["JAVA_HOME=${tool "jdk8"}"] ) {
unstash name: 'perf-tests' unstash name: 'perf-tests'
sh "${env.JAVA_HOME}/bin/java -jar jetty-jmh/target/benchmarks.jar -rff jetty-jmh/target/jmh_result.json -rf json" sh "rm -rf jmh_results"
jmhReport 'jetty-jmh/target/jmh_result.json' sh "mkdir jmh_results"
sh "${env.JAVA_HOME}/bin/java -jar $jmhJarPath -rff jmh_results/jmh_result.json -rf json -foe true"
jmhReport 'jmh_results/jmh_result.json'
} }
} }
} }

View File

@ -1,7 +1,8 @@
This program and the accompanying materials are made available under the This program and the accompanying materials are made available under the
terms of the Eclipse Public License 2.0 which is available at terms of the Eclipse Public License 1.0 which is available at
http://www.eclipse.org/legal/epl-2.0, or the Apache Software License https://www.eclipse.org/org/documents/epl-1.0/EPL-1.0.txt
2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0. or the Apache Software License 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0

View File

@ -1,5 +1,49 @@
jetty-10.0.0-SNAPSHOT jetty-10.0.0-SNAPSHOT
jetty-9.4.18.v20190429 - 29 April 2019
+ 3476 IllegalStateException in WebSocket ConnectionState
+ 3550 Server becomes unresponsive after sitting idle from a load spike
+ 3563 Update to apache jasper 8.5.40
+ 3573 Update jetty-bom for new infinispan artifacts.
+ 3582 HeapByteBuffer cleared unexpected
+ 3597 Session persistence broken from 9.4.13+
+ 3609 Fix infinispan start module dependencies
jetty-9.4.17.v20190418 - 18 April 2019
+ 3464 Split SslContextFactory into Client and Server
+ 3549 Directory Listing on Windows reveals Resource Base path
+ 3555 DefaultHandler Reveals Base Resource Path of each Context
jetty-9.4.16.v20190411 - 11 April 2019
+ 1861 Limit total bytes pooled by ByteBufferPools
+ 3133 Logging of `key.readyOps()` can throw unchecked `CancelledKeyException`
+ 3159 WebSocket permessage-deflate RSV1 validity check
+ 3274 OSGi versions of java.base classes in
org.apache.felix:org.osgi.foundation:jar conflicts with new rules on Java 9+
+ 3319 Modernize Directory Listing: HTML5 and Sorting
+ 3361 HandlerCollection.addHandler is lacking synchronization
+ 3373 OutOfMemoryError: Java heap space in GZIPContentDecoder
+ 3389 Websockets jsr356 willDecode not invoked during decoding
+ 3394 java.security.acl.Group is deprecated and marked for removal
+ 3404 Cleanup QuotedQualityCSV internal use of Double
+ 3411 HttpClient does not timeout during multiple redirection
+ 3421 Duplicate JSESSIONID sent when invalidating new session
+ 3422 CLOSE_WAIT socket status forever after terminating websocket client
side
+ 3425 Upgrade conscrypt version to 2.0.0 and remove usage of reflection
+ 3429 JMX Operation to trigger manual deployment scan in WebAppProvider
+ 3440 Stop server if Unavailable thrown
+ 3444 org.eclipse.jetty.http.Http1FieldPreEncoder generates an invalid header
byte-array if header is null
+ 3456 Allow multiple programmatic login/logout in same request
+ 3464 Split SslContextFactory into Client and Server
+ 3481 TLS close_notify() is not guaranteed
+ 3489 Using setExtraClasspath("lib/extra/*") does not work on Microsoft
Windows
+ 3526 HTTP Request Locale not retained in WebsocketUpgrade Request
+ 3540 Use configured Provider in SslContextFactory consistently
+ 3545 NullPointerException on ServletOutputStream.print("");
jetty-9.4.15.v20190215 - 15 February 2019 jetty-9.4.15.v20190215 - 15 February 2019
+ 113 Add support for NCSA Extended Log File Format + 113 Add support for NCSA Extended Log File Format
+ 150 extraClasspath() method on WebAppContext dont support dir path + 150 extraClasspath() method on WebAppContext dont support dir path
@ -43,6 +87,25 @@ jetty-9.4.15.v20190215 - 15 February 2019
+ 3350 Do not expect to be able to connect to https URLs with the HttpClient + 3350 Do not expect to be able to connect to https URLs with the HttpClient
created from a parameterless constructor created from a parameterless constructor
jetty-9.3.27.v20190418 - 18 April 2019
+ 3549 Directory Listing on Windows reveals Resource Base path
+ 3555 DefaultHandler Reveals Base Resource Path of each Context
jetty-9.3.26.v20190403 - 03 April 2019
+ 2954 Improve cause reporting for HttpClient failures
+ 3274 OSGi versions of java.base classes in
org.apache.felix:org.osgi.foundation:jar conflicts with new rules on Java 9+
+ 3302 Support host:port in X-Forwarded-For header in
ForwardedRequestCustomizer
+ 3319 Allow reverse sort for directory listed files
jetty-9.2.28.v20190418 - 18 April 2019
+ 3549 Directory Listing on Windows reveals Resource Base path
+ 3555 DefaultHandler Reveals Base Resource Path of each Context
jetty-9.2.27.v20190403 - 03 April 2019
+ 3319 Refactored Directory Listing to modernize and avoid XSS
jetty-9.4.14.v20181114 - 14 November 2018 jetty-9.4.14.v20181114 - 14 November 2018
+ 3097 Duplicated programmatic Servlet Listeners causing duplicate calls + 3097 Duplicated programmatic Servlet Listeners causing duplicate calls
+ 3103 HttpClientLoadTest reports a leak in byte buffer + 3103 HttpClientLoadTest reports a leak in byte buffer
@ -90,6 +153,13 @@ jetty-9.4.13.v20181111 - 11 November 2018
+ 3090 MBeanContainer throws NPE for arrays + 3090 MBeanContainer throws NPE for arrays
+ 3092 Wrong classloader used to load *MBean classes + 3092 Wrong classloader used to load *MBean classes
jetty-9.3.25.v20180904 - 04 September 2018
+ 2135 Android 8.1 needs direct buffers for SSL/TLS to work
+ 2777 Workaround for Conscrypt's ssl == null
+ 2787 BadMessageException wrapped as ServletException not handled
+ 2860 Leakage of HttpDestinations in HttpClient
+ 2871 Server reads -1 after client resets HTTP/2 stream
jetty-9.4.12.v20180830 - 30 August 2018 jetty-9.4.12.v20180830 - 30 August 2018
+ 300 Implement Deflater / Inflater Object Pool + 300 Implement Deflater / Inflater Object Pool
+ 307 Monitor contention in AbstractNCSARequestLog + 307 Monitor contention in AbstractNCSARequestLog
@ -182,13 +252,6 @@ jetty-9.4.12.v20180830 - 30 August 2018
+ 2860 Leakage of HttpDestinations in HttpClient + 2860 Leakage of HttpDestinations in HttpClient
+ 2871 Server reads -1 after client resets HTTP/2 stream + 2871 Server reads -1 after client resets HTTP/2 stream
jetty-9.3.25.v20180904 - 04 September 2018
+ 2135 Android 8.1 needs direct buffers for SSL/TLS to work
+ 2777 Workaround for Conscrypt's ssl == null
+ 2787 BadMessageException wrapped as ServletException not handled
+ 2860 Leakage of HttpDestinations in HttpClient
+ 2871 Server reads -1 after client resets HTTP/2 stream
jetty-9.2.26.v20180806 - 06 August 2018 jetty-9.2.26.v20180806 - 06 August 2018
+ 2777 Workaround for Conscrypt's ssl == null + 2777 Workaround for Conscrypt's ssl == null

View File

@ -209,8 +209,8 @@
</dependency> </dependency>
<!-- dependencies that jetty-all needs (some optional) --> <!-- dependencies that jetty-all needs (some optional) -->
<dependency> <dependency>
<groupId>javax.websocket</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>javax.websocket-api</artifactId> <artifactId>jetty-javax-websocket-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>

View File

@ -144,8 +144,8 @@
</dependency> </dependency>
<!-- dependencies that jetty-all needs (some optional) --> <!-- dependencies that jetty-all needs (some optional) -->
<dependency> <dependency>
<groupId>javax.websocket</groupId> <groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>javax.websocket-api</artifactId> <artifactId>jetty-javax-websocket-api</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -119,7 +119,7 @@ public class JettyJspServlet extends JspServlet
*/ */
private String addPaths(String servletPath, String pathInfo) private String addPaths(String servletPath, String pathInfo)
{ {
if (servletPath.length()==0) if (servletPath.isEmpty())
return pathInfo; return pathInfo;
if (pathInfo==null) if (pathInfo==null)

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- <!--
This is the jetty specific web application configuration file. When starting This is the jetty specific web application configuration file. When starting
@ -9,7 +9,7 @@ org.eclipse.jetty.servlet.WebApplicationContext object
--> -->
<Configure class="org.eclipse.jetty.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get class="org.eclipse.jetty.util.log.Log" name="rootLogger"> <Get name="servletContext">
<Call name="warn"><Arg>async-rest webapp is deployed. DO NOT USE IN PRODUCTION!</Arg></Call> <Call name="log"><Arg>The async-rest webapp is deployed. DO NOT USE IN PRODUCTION!</Arg><Arg></Arg></Call>
</Get> </Get>
</Configure> </Configure>

View File

@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode; import java.nio.channels.FileChannel.MapMode;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -116,7 +115,8 @@ public class FastFileServer
} }
String listing = Resource.newResource(file).getListHTML( String listing = Resource.newResource(file).getListHTML(
request.getRequestURI(), request.getRequestURI(),
request.getPathInfo().lastIndexOf("/") > 0); request.getPathInfo().lastIndexOf("/") > 0,
request.getQueryString());
response.setContentType("text/html; charset=utf-8"); response.setContentType("text/html; charset=utf-8");
response.getWriter().println(listing); response.getWriter().println(listing);
return; return;

View File

@ -36,9 +36,8 @@ public class FileServerXml
{ {
public static void main( String[] args ) throws Exception public static void main( String[] args ) throws Exception
{ {
Resource fileserverXml = Resource.newSystemResource("fileserver.xml"); Resource fileServerXml = Resource.newSystemResource("fileserver.xml");
XmlConfiguration configuration = new XmlConfiguration( XmlConfiguration configuration = new XmlConfiguration(fileServerXml);
fileserverXml.getInputStream());
Server server = (Server) configuration.configure(); Server server = (Server) configuration.configure();
server.start(); server.start();
server.join(); server.join();

View File

@ -60,10 +60,6 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.PushCacheFilter; import org.eclipse.jetty.servlets.PushCacheFilter;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
/* ------------------------------------------------------------ */
/**
*/
public class Http2Server public class Http2Server
{ {
public static void main(String... args) throws Exception public static void main(String... args) throws Exception
@ -102,7 +98,7 @@ public class Http2Server
String jetty_distro = System.getProperty("jetty.distro","../../jetty-distribution/target/distribution"); String jetty_distro = System.getProperty("jetty.distro","../../jetty-distribution/target/distribution");
if (!new File(jetty_distro).exists()) if (!new File(jetty_distro).exists())
jetty_distro = "jetty-distribution/target/distribution"; jetty_distro = "jetty-distribution/target/distribution";
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(jetty_distro + "/demo-base/etc/keystore"); sslContextFactory.setKeyStorePath(jetty_distro + "/demo-base/etc/keystore");
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

View File

@ -99,7 +99,7 @@ public class LikeJettyXml
Server server = new Server(threadPool); Server server = new Server(threadPool);
// Scheduler // Scheduler
server.addBean(new ScheduledExecutorScheduler()); server.addBean(new ScheduledExecutorScheduler(null,false));
// HTTP Configuration // HTTP Configuration
HttpConfiguration http_config = new HttpConfiguration(); HttpConfiguration http_config = new HttpConfiguration();
@ -139,7 +139,7 @@ public class LikeJettyXml
// === jetty-https.xml === // === jetty-https.xml ===
// SSL Context Factory // SSL Context Factory
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(jetty_home + "/../../../jetty-server/src/test/config/etc/keystore"); sslContextFactory.setKeyStorePath(jetty_home + "/../../../jetty-server/src/test/config/etc/keystore");
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

View File

@ -20,9 +20,7 @@ package org.eclipse.jetty.embedded;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.security.Security;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
@ -89,7 +87,7 @@ public class ManyConnectors
// including things like choosing the particular certificate out of a // including things like choosing the particular certificate out of a
// keystore to be used. // keystore to be used.
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

View File

@ -79,7 +79,7 @@ public class ManyHandlers
ServletException ServletException
{ {
Map<String, String[]> params = request.getParameterMap(); Map<String, String[]> params = request.getParameterMap();
if (params.size() > 0) if (!params.isEmpty())
{ {
response.setContentType("text/plain"); response.setContentType("text/plain");
response.getWriter().println(JSON.toString(params)); response.getWriter().println(JSON.toString(params));

View File

@ -23,7 +23,6 @@ import java.lang.management.ManagementFactory;
import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
import org.eclipse.jetty.webapp.Configurations; import org.eclipse.jetty.webapp.Configurations;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
@ -65,7 +64,7 @@ public class OneWebApp
Configurations.setServerDefault(server); Configurations.setServerDefault(server);
// Start things up! // Start things up!
server.start(); server.start();
server.dumpStdErr(); server.dumpStdErr();

View File

@ -24,10 +24,8 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.core.FrameHandler; import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketMapping; import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
/** /**
* Example of setting up a Jetty WebSocket server * Example of setting up a Jetty WebSocket server
@ -53,10 +51,10 @@ public class WebSocketServer
* Servlet layer * Servlet layer
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public static class EchoServlet extends WebSocketServlet public static class EchoServlet extends JettyWebSocketServlet
{ {
@Override @Override
public void configure(WebSocketServletFactory factory) public void configure(JettyWebSocketServletFactory factory)
{ {
factory.addMapping(factory.parsePathSpec("/"), (req,res)->new EchoSocket()); factory.addMapping(factory.parsePathSpec("/"), (req,res)->new EchoSocket());
} }

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="ExampleServer" class="org.eclipse.jetty.server.Server"> <Configure id="ExampleServer" class="org.eclipse.jetty.server.Server">

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="FileServer" class="org.eclipse.jetty.server.Server"> <Configure id="FileServer" class="org.eclipse.jetty.server.Server">

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="OtherServer" class="org.eclipse.jetty.server.Server"> <Configure id="OtherServer" class="org.eclipse.jetty.server.Server">
<Set name="handler"> <Set name="handler">

View File

@ -19,6 +19,14 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<!-- No point deploying example projects -->
<skip>true</skip>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId> <artifactId>findbugs-maven-plugin</artifactId>

View File

@ -48,6 +48,6 @@ public class ALPNClientConnection extends NegotiatingClientConnection
if (protocol == null || !protocols.contains(protocol)) if (protocol == null || !protocols.contains(protocol))
close(); close();
else else
completed(); completed(protocol);
} }
} }

View File

@ -111,4 +111,12 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
} }
throw new IllegalStateException("No ALPNProcessor for " + engine); throw new IllegalStateException("No ALPNProcessor for " + engine);
} }
public static class ALPN extends Info
{
public ALPN(Executor executor, ClientConnectionFactory factory, List<String> protocols)
{
super(List.of("alpn"), new ALPNClientConnectionFactory(executor, factory, protocols));
}
}
} }

View File

@ -18,12 +18,10 @@
package org.eclipse.jetty.alpn.conscrypt.client; package org.eclipse.jetty.alpn.conscrypt.client;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.security.Security; import java.security.Security;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import org.conscrypt.Conscrypt;
import org.conscrypt.OpenSSLProvider; import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.alpn.client.ALPNClientConnection; import org.eclipse.jetty.alpn.client.ALPNClientConnection;
import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Connection;
@ -40,7 +38,7 @@ public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
@Override @Override
public void init() public void init()
{ {
if (Security.getProvider("Conscrypt")==null) if (Security.getProvider("Conscrypt") == null)
{ {
Security.addProvider(new OpenSSLProvider()); Security.addProvider(new OpenSSLProvider());
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -59,11 +57,9 @@ public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
{ {
try try
{ {
Method setAlpnProtocols = sslEngine.getClass().getDeclaredMethod("setApplicationProtocols", String[].class);
setAlpnProtocols.setAccessible(true);
ALPNClientConnection alpn = (ALPNClientConnection)connection; ALPNClientConnection alpn = (ALPNClientConnection)connection;
String[] protocols = alpn.getProtocols().toArray(new String[0]); String[] protocols = alpn.getProtocols().toArray(new String[0]);
setAlpnProtocols.invoke(sslEngine, (Object)protocols); Conscrypt.setApplicationProtocols(sslEngine, protocols);
((SslConnection.DecryptedEndPoint)connection.getEndPoint()).getSslConnection() ((SslConnection.DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
.addHandshakeListener(new ALPNListener(alpn)); .addHandshakeListener(new ALPNListener(alpn));
} }
@ -92,9 +88,9 @@ public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
try try
{ {
SSLEngine sslEngine = alpnConnection.getSSLEngine(); SSLEngine sslEngine = alpnConnection.getSSLEngine();
Method method = sslEngine.getClass().getDeclaredMethod("getApplicationProtocol"); String protocol = Conscrypt.getApplicationProtocol(sslEngine);
method.setAccessible(true); if (LOG.isDebugEnabled())
String protocol = (String)method.invoke(sslEngine); LOG.debug("Selected {} for {}", protocol, alpnConnection);
alpnConnection.selected(protocol); alpnConnection.selected(protocol);
} }
catch (Throwable e) catch (Throwable e)

View File

@ -1,89 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.alpn.java.client;
import java.net.InetSocketAddress;
import java.security.Security;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
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.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class ConscryptHTTP2Client
{
public static void main(String[] args) throws Exception
{
Security.addProvider(new OpenSSLProvider());
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setProvider("Conscrypt");
HTTP2Client client = new HTTP2Client();
client.addBean(sslContextFactory);
client.start();
String host = "webtide.com";
int port = 443;
FuturePromise<Session> sessionPromise = new FuturePromise<>();
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
HttpFields requestFields = new HttpFields();
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
CountDownLatch latch = new CountDownLatch(1);
session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
System.err.println(frame);
if (frame.isEndStream())
latch.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
System.err.println(frame);
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
latch.await(5, TimeUnit.SECONDS);
client.stop();
}
}

View File

@ -0,0 +1,122 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.alpn.java.client;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.Security;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.conscrypt.Conscrypt;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
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.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ConscryptHTTP2ClientTest
{
@Tag("external")
@Test
@Disabled("issue google/conscrypt#667")
public void testConscryptHTTP2Client() throws Exception
{
String host = "webtide.com";
int port = 443;
Assumptions.assumeTrue(canConnectTo(host, port));
Security.insertProviderAt(new OpenSSLProvider(), 1);
SslContextFactory sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setProvider("Conscrypt");
Conscrypt.setDefaultHostnameVerifier((hostname, session) -> true);
HTTP2Client client = new HTTP2Client();
try
{
client.addBean(sslContextFactory);
client.start();
FuturePromise<Session> sessionPromise = new FuturePromise<>();
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
Session session = sessionPromise.get(15, TimeUnit.SECONDS);
HttpFields requestFields = new HttpFields();
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
CountDownLatch latch = new CountDownLatch(1);
session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
System.err.println(frame);
if (frame.isEndStream())
latch.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
System.err.println(frame);
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
assertTrue(latch.await(15, TimeUnit.SECONDS));
}
finally
{
client.stop();
}
}
private boolean canConnectTo(String host, int port)
{
try
{
new Socket(host, port).close();
return true;
}
catch (Throwable x)
{
return false;
}
}
}

View File

@ -38,23 +38,57 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-conscrypt-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-http-client-transport</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.felix</groupId> <groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId> <artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<instructions> <instructions>
<Bundle-Description>Conscrypt ALPN</Bundle-Description> <Bundle-Description>Conscrypt ALPN</Bundle-Description>
<Import-Package>org.conscrypt;version="${conscrypt.version}",*</Import-Package> <Import-Package>org.conscrypt;version="${conscrypt.version}",*</Import-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability> <Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
<Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server</Provide-Capability> <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server</Provide-Capability>
<_nouses>true</_nouses> <_nouses>true</_nouses>
</instructions> </instructions>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine}
--add-reads org.eclipse.jetty.alpn.conscrypt.server=org.eclipse.jetty.server
</argLine>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -18,13 +18,13 @@
package org.eclipse.jetty.alpn.conscrypt.server; package org.eclipse.jetty.alpn.conscrypt.server;
import java.lang.reflect.Method;
import java.security.Security; import java.security.Security;
import java.util.List; import java.util.List;
import java.util.function.BiFunction;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import org.conscrypt.ApplicationProtocolSelector;
import org.conscrypt.Conscrypt;
import org.conscrypt.OpenSSLProvider; import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.alpn.server.ALPNServerConnection; import org.eclipse.jetty.alpn.server.ALPNServerConnection;
import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Connection;
@ -41,7 +41,7 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
@Override @Override
public void init() public void init()
{ {
if (Security.getProvider("Conscrypt")==null) if (Security.getProvider("Conscrypt") == null)
{ {
Security.addProvider(new OpenSSLProvider()); Security.addProvider(new OpenSSLProvider());
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -56,13 +56,11 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
} }
@Override @Override
public void configure(SSLEngine sslEngine,Connection connection) public void configure(SSLEngine sslEngine, Connection connection)
{ {
try try
{ {
Method method = sslEngine.getClass().getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class); Conscrypt.setApplicationProtocolSelector(sslEngine, new ALPNCallback((ALPNServerConnection)connection));
method.setAccessible(true);
method.invoke(sslEngine,new ALPNCallback((ALPNServerConnection)connection));
} }
catch (RuntimeException x) catch (RuntimeException x)
{ {
@ -74,23 +72,31 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
} }
} }
private final class ALPNCallback implements BiFunction<SSLEngine,List<String>,String>, SslHandshakeListener private final class ALPNCallback extends ApplicationProtocolSelector implements SslHandshakeListener
{ {
private final ALPNServerConnection alpnConnection; private final ALPNServerConnection alpnConnection;
private ALPNCallback(ALPNServerConnection connection) private ALPNCallback(ALPNServerConnection connection)
{ {
alpnConnection = connection; alpnConnection = connection;
((DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this); ((DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this);
} }
@Override @Override
public String apply(SSLEngine engine, List<String> protocols) public String selectApplicationProtocol(SSLEngine engine, List<String> protocols)
{ {
if (LOG.isDebugEnabled())
LOG.debug("apply {} {}", alpnConnection, protocols);
alpnConnection.select(protocols); alpnConnection.select(protocols);
return alpnConnection.getProtocol(); String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled())
LOG.debug("Selected {} among {} for {}", protocol, protocols, alpnConnection);
return protocol;
}
@Override
public String selectApplicationProtocol(SSLSocket socket, List<String> protocols)
{
throw new UnsupportedOperationException();
} }
@Override @Override
@ -99,7 +105,7 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
String protocol = alpnConnection.getProtocol(); String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("TLS handshake succeeded, protocol={} for {}", protocol, alpnConnection); LOG.debug("TLS handshake succeeded, protocol={} for {}", protocol, alpnConnection);
if (protocol ==null) if (protocol == null)
alpnConnection.unsupported(); alpnConnection.unsupported();
} }

View File

@ -1,72 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.alpn.conscrypt.server;
import java.security.Security;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
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.util.ssl.SslContextFactory;
/**
* Test server that verifies that the Conscrypt ALPN mechanism works.
*/
public class ConscryptHTTP2Server
{
public static void main(String[] args) throws Exception
{
Security.addProvider(new OpenSSLProvider());
Server server = new Server();
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.setSecureScheme("https");
httpsConfig.setSecurePort(8443);
httpsConfig.setSendXPoweredBy(true);
httpsConfig.setSendServerVersion(true);
httpsConfig.addCustomizer(new SecureRequestCustomizer());
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setProvider("Conscrypt");
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(http.getProtocol());
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
http2Connector.setPort(8443);
server.addConnector(http2Connector);
server.start();
}
}

View File

@ -0,0 +1,154 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.alpn.conscrypt.server;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Security;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
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.AbstractHandler;
import org.eclipse.jetty.util.JavaVersion;
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.junit.jupiter.api.Assertions.assertEquals;
/**
* Test server that verifies that the Conscrypt ALPN mechanism works for both server and client side
*/
public class ConscryptHTTP2ServerTest
{
static
{
Security.addProvider(new OpenSSLProvider());
}
private Server server = new Server();
private SslContextFactory.Server newServerSslContextFactory()
{
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
configureSslContextFactory(sslContextFactory);
return sslContextFactory;
}
private SslContextFactory.Client newClientSslContextFactory()
{
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
configureSslContextFactory(sslContextFactory);
sslContextFactory.setEndpointIdentificationAlgorithm(null);
return sslContextFactory;
}
private void configureSslContextFactory(SslContextFactory sslContextFactory)
{
Path path = Paths.get("src", "test", "resources");
File keys = path.resolve("keystore").toFile();
sslContextFactory.setKeyStorePath(keys.getAbsolutePath());
sslContextFactory.setKeyManagerPassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setTrustStorePath(keys.getAbsolutePath());
sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setProvider("Conscrypt");
if (JavaVersion.VERSION.getPlatform() < 9)
{
// Conscrypt enables TLSv1.3 by default but it's not supported in Java 8.
sslContextFactory.addExcludeProtocols("TLSv1.3");
}
}
@BeforeEach
public void startServer() throws Exception
{
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.setSecureScheme("https");
httpsConfig.setSendXPoweredBy(true);
httpsConfig.setSendServerVersion(true);
httpsConfig.addCustomizer(new SecureRequestCustomizer());
HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(http.getProtocol());
SslConnectionFactory ssl = new SslConnectionFactory(newServerSslContextFactory(), alpn.getProtocol());
ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
http2Connector.setPort(0);
server.addConnector(http2Connector);
server.setHandler(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{
response.setStatus(200);
baseRequest.setHandled(true);
}
});
server.start();
}
@AfterEach
public void stopServer() throws Exception
{
if (server != null)
server.stop();
}
@Test
public void testSimpleRequest() throws Exception
{
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSslContextFactory(newClientSslContextFactory());
HTTP2Client h2Client = new HTTP2Client(clientConnector);
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(h2Client));
client.start();
try
{
int port = ((ServerConnector)server.getConnectors()[0]).getLocalPort();
ContentResponse contentResponse = client.GET("https://localhost:" + port);
assertEquals(200, contentResponse.getStatus());
}
finally
{
client.stop();
}
}
}

View File

@ -1,82 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.alpn.java.client;
import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
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.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
public class JDK9HTTP2Client
{
public static void main(String[] args) throws Exception
{
HTTP2Client client = new HTTP2Client();
client.start();
String host = "webtide.com";
int port = 443;
FuturePromise<Session> sessionPromise = new FuturePromise<>();
client.connect(client.getClientConnector().getSslContextFactory(), new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
HttpFields requestFields = new HttpFields();
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
CountDownLatch latch = new CountDownLatch(1);
session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
System.err.println(frame);
if (frame.isEndStream())
latch.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
System.err.println(frame);
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
latch.await(5, TimeUnit.SECONDS);
client.stop();
}
}

View File

@ -0,0 +1,111 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.alpn.java.client;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
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.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
public class JDK9HTTP2ClientTest
{
@Tag("external")
@Test
public void testJDK9HTTP2Client() throws Exception
{
String host = "webtide.com";
int port = 443;
Assumptions.assumeTrue(canConnectTo(host, port));
HTTP2Client client = new HTTP2Client();
try
{
SslContextFactory sslContextFactory = new SslContextFactory.Client();
client.addBean(sslContextFactory);
client.start();
FuturePromise<Session> sessionPromise = new FuturePromise<>();
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
Session session = sessionPromise.get(15, TimeUnit.SECONDS);
HttpFields requestFields = new HttpFields();
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
CountDownLatch latch = new CountDownLatch(1);
session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
{
@Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
System.err.println(frame);
if (frame.isEndStream())
latch.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
System.err.println(frame);
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
latch.await(15, TimeUnit.SECONDS);
}
finally
{
client.stop();
}
}
private boolean canConnectTo(String host, int port)
{
try
{
new Socket(host, port).close();
return true;
}
catch (Throwable x)
{
return false;
}
}
}

View File

@ -59,7 +59,7 @@ public class JDK9ALPNTest
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpConfiguration); HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpConfiguration);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory(); ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(h1.getProtocol()); alpn.setDefaultProtocol(h1.getProtocol());
connector = new ServerConnector(server, newSslContextFactory(), alpn, h1, h2); connector = new ServerConnector(server, newServerSslContextFactory(), alpn, h1, h2);
server.addConnector(connector); server.addConnector(connector);
server.setHandler(handler); server.setHandler(handler);
server.start(); server.start();
@ -72,13 +72,12 @@ public class JDK9ALPNTest
server.stop(); server.stop();
} }
private SslContextFactory newSslContextFactory() private SslContextFactory.Server newServerSslContextFactory()
{ {
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
sslContextFactory.setIncludeProtocols("TLSv1.2");
// The mandatory HTTP/2 cipher. // The mandatory HTTP/2 cipher.
sslContextFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); sslContextFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
return sslContextFactory; return sslContextFactory;
@ -96,7 +95,7 @@ public class JDK9ALPNTest
} }
}); });
SslContextFactory sslContextFactory = new SslContextFactory(true); SslContextFactory sslContextFactory = new SslContextFactory.Client(true);
sslContextFactory.start(); sslContextFactory.start();
SSLContext sslContext = sslContextFactory.getSslContext(); SSLContext sslContext = sslContextFactory.getSslContext();
try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort())) try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort()))
@ -138,7 +137,7 @@ public class JDK9ALPNTest
} }
}); });
SslContextFactory sslContextFactory = new SslContextFactory(true); SslContextFactory sslContextFactory = new SslContextFactory.Client(true);
sslContextFactory.start(); sslContextFactory.start();
SSLContext sslContext = sslContextFactory.getSslContext(); SSLContext sslContext = sslContextFactory.getSslContext();
try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort())) try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort()))

View File

@ -45,7 +45,7 @@ public class JDK9HTTP2Server
httpsConfig.setSendServerVersion(true); httpsConfig.setSendServerVersion(true);
httpsConfig.addCustomizer(new SecureRequestCustomizer()); httpsConfig.addCustomizer(new SecureRequestCustomizer());
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector"> <Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">

View File

@ -18,14 +18,6 @@
package org.eclipse.jetty.annotations; package org.eclipse.jetty.annotations;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.isIn;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -55,6 +47,15 @@ import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(WorkDirExtension.class) @ExtendWith(WorkDirExtension.class)
public class TestAnnotationParser public class TestAnnotationParser
{ {
@ -142,7 +143,7 @@ public class TestAnnotationParser
if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation))
return; return;
assertEquals("org.eclipse.jetty.annotations.ClassA",info.getClassInfo().getClassName()); assertEquals("org.eclipse.jetty.annotations.ClassA",info.getClassInfo().getClassName());
assertThat(info.getMethodName(), isIn(methods)); assertThat(info.getMethodName(), is(in(methods)));
assertEquals("org.eclipse.jetty.annotations.Sample",annotation); assertEquals("org.eclipse.jetty.annotations.Sample",annotation);
} }
} }

View File

@ -52,6 +52,7 @@ import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.servlet.Source; import org.eclipse.jetty.servlet.Source;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.MetaInfConfiguration; import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppClassLoader;
@ -592,7 +593,7 @@ public class AntWebAppContext extends WebAppContext
TaskLog.logWithTimestamp("Starting web application "+this.getDescriptor()); TaskLog.logWithTimestamp("Starting web application "+this.getDescriptor());
if (jettyEnvXml != null && jettyEnvXml.exists()) if (jettyEnvXml != null && jettyEnvXml.exists())
getConfiguration(EnvConfiguration.class).setJettyEnvXml(Resource.toURL(jettyEnvXml)); getConfiguration(EnvConfiguration.class).setJettyEnvResource(new PathResource(jettyEnvXml));
ClassLoader parentLoader = this.getClass().getClassLoader(); ClassLoader parentLoader = this.getClass().getClassLoader();
if (parentLoader instanceof AntClassLoader) if (parentLoader instanceof AntClassLoader)
@ -608,7 +609,7 @@ public class AntWebAppContext extends WebAppContext
//apply a context xml file if one was supplied //apply a context xml file if one was supplied
if (contextXml != null) if (contextXml != null)
{ {
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(contextXml)); XmlConfiguration xmlConfiguration = new XmlConfiguration(new PathResource(contextXml));
TaskLog.log("Applying context xml file "+contextXml); TaskLog.log("Applying context xml file "+contextXml);
xmlConfiguration.configure(this); xmlConfiguration.configure(this);
} }

View File

@ -41,6 +41,7 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration; import org.eclipse.jetty.xml.XmlConfiguration;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -451,7 +452,7 @@ public class ServerProxyImpl implements ServerProxy
XmlConfiguration configuration; XmlConfiguration configuration;
try try
{ {
configuration = new XmlConfiguration(Resource.toURL(jettyXml)); configuration = new XmlConfiguration(new PathResource(jettyXml));
configuration.configure(server); configuration.configure(server);
} }
catch (MalformedURLException e) catch (MalformedURLException e)

View File

@ -186,7 +186,17 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-infinispan</artifactId> <artifactId>infinispan-common</artifactId>
<version>10.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>infinispan-remote-query</artifactId>
<version>10.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>infinispan-embedded-query</artifactId>
<version>10.0.0-SNAPSHOT</version> <version>10.0.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- =============================================================== --> <!-- =============================================================== -->
<!-- Mixin the Weld / CDI classes to the class loader --> <!-- Mixin the Weld / CDI classes to the class loader -->

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get name="serverClasspathPattern">
<Call name="prependServerClass"> <Call name="add">
<Arg>-org.eclipse.jetty.util.Decorator</Arg> <Arg>-org.eclipse.jetty.util.Decorator</Arg>
</Call> </Call>
<Call name="prependServerClass"> <Call name="add">
<Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg> <Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg>
</Call> </Call>
<Call name="prependServerClass"> <Call name="add">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg> <Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg>
</Call> </Call>
<Call name="prependServerClass"> <Call name="add">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg> <Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg>
</Call> </Call>
<Call name="prependServerClass"> <Call name="add">
<Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg> <Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg>
</Call> </Call>
</Get>
</Configure> </Configure>

View File

@ -0,0 +1,45 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.cdi.core;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Unit tests for class {@link NamedLiteral}.
*
* @see NamedLiteral
*/
public class NamedLiteralTest
{
@Test
public void testCreatesNamedLiteralWithNull()
{
assertEquals("", new NamedLiteral(null).value());
}
@Test
public void testGetValue()
{
assertEquals("a b", new NamedLiteral("a b").value());
}
}

View File

@ -112,6 +112,12 @@
<artifactId>jetty-io</artifactId> <artifactId>jetty-io</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-client</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId> <artifactId>jetty-jmx</artifactId>

View File

@ -20,8 +20,10 @@ module org.eclipse.jetty.client
{ {
exports org.eclipse.jetty.client; exports org.eclipse.jetty.client;
exports org.eclipse.jetty.client.api; exports org.eclipse.jetty.client.api;
exports org.eclipse.jetty.client.dynamic;
exports org.eclipse.jetty.client.http; exports org.eclipse.jetty.client.http;
exports org.eclipse.jetty.client.jmx to org.eclipse.jetty.jmx; exports org.eclipse.jetty.client.jmx to org.eclipse.jetty.jmx;
exports org.eclipse.jetty.client.proxy;
exports org.eclipse.jetty.client.util; exports org.eclipse.jetty.client.util;
requires org.eclipse.jetty.http; requires org.eclipse.jetty.http;
@ -30,6 +32,8 @@ module org.eclipse.jetty.client
// Only required if using SPNEGO. // Only required if using SPNEGO.
requires static java.security.jgss; requires static java.security.jgss;
// Only required if using the dynamic transport.
requires static org.eclipse.jetty.alpn.client;
// Only required if using JMX. // Only required if using JMX.
requires static org.eclipse.jetty.jmx; requires static org.eclipse.jetty.jmx;
} }

View File

@ -119,14 +119,14 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("newConnection {}/{} connections {}/{} pending", total+1, maxConnections, pending+1, maxPending); LOG.debug("newConnection {}/{} connections {}/{} pending", total+1, maxConnections, pending+1, maxPending);
destination.newConnection(new Promise<Connection>() destination.newConnection(new Promise<>()
{ {
@Override @Override
public void succeeded(Connection connection) public void succeeded(Connection connection)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Connection {}/{} creation succeeded {}", total+1, maxConnections, connection); LOG.debug("Connection {}/{} creation succeeded {}", total + 1, maxConnections, connection);
connections.add(-1,0); connections.add(-1, 0);
onCreated(connection); onCreated(connection);
proceed(); proceed();
} }
@ -135,8 +135,8 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
public void failed(Throwable x) public void failed(Throwable x)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Connection " + (total+1) + "/" + maxConnections + " creation failed", x); LOG.debug("Connection " + (total + 1) + "/" + maxConnections + " creation failed", x);
connections.add(-1,-1); connections.add(-1, -1);
requester.failed(x); requester.failed(x);
} }
}); });

View File

@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -217,6 +218,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
path = request.getPath(); path = request.getPath();
} }
Request newRequest = client.copyRequest(request, requestURI); Request newRequest = client.copyRequest(request, requestURI);
// Disable the timeout so that only the one from the initial request applies.
newRequest.timeout(0, TimeUnit.MILLISECONDS);
if (path != null) if (path != null)
newRequest.path(path); newRequest.path(path);

View File

@ -0,0 +1,37 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client;
/**
* <p>A destination for those network transports that are duplex (e.g. HTTP/1.1 and FastCGI).</p>
*
* @see MultiplexHttpDestination
*/
public class DuplexHttpDestination extends HttpDestination
{
public DuplexHttpDestination(HttpClient client, Origin origin)
{
this(client, new Key(origin, null));
}
public DuplexHttpDestination(HttpClient client, Key key)
{
super(client, key);
}
}

View File

@ -28,6 +28,7 @@ import java.net.SocketAddress;
import java.net.URI; import java.net.URI;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -61,6 +62,7 @@ import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Fields;
@ -122,19 +124,16 @@ public class HttpClient extends ContainerLifeCycle
public static final String USER_AGENT = "Jetty/" + Jetty.VERSION; public static final String USER_AGENT = "Jetty/" + Jetty.VERSION;
private static final Logger LOG = Log.getLogger(HttpClient.class); private static final Logger LOG = Log.getLogger(HttpClient.class);
private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>(); private final ConcurrentMap<HttpDestination.Key, HttpDestination> destinations = new ConcurrentHashMap<>();
private final ProtocolHandlers handlers = new ProtocolHandlers(); private final ProtocolHandlers handlers = new ProtocolHandlers();
private final List<Request.Listener> requestListeners = new ArrayList<>(); private final List<Request.Listener> requestListeners = new ArrayList<>();
private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet(); private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
private final ProxyConfiguration proxyConfig = new ProxyConfiguration(); private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
private final HttpClientTransport transport; private final HttpClientTransport transport;
private final SslContextFactory sslContextFactory; private final ClientConnector connector;
private AuthenticationStore authenticationStore = new HttpAuthenticationStore(); private AuthenticationStore authenticationStore = new HttpAuthenticationStore();
private CookieManager cookieManager; private CookieManager cookieManager;
private CookieStore cookieStore; private CookieStore cookieStore;
private Executor executor;
private ByteBufferPool byteBufferPool;
private Scheduler scheduler;
private SocketAddressResolver resolver; private SocketAddressResolver resolver;
private HttpField agentField = new HttpField(HttpHeader.USER_AGENT, USER_AGENT); private HttpField agentField = new HttpField(HttpHeader.USER_AGENT, USER_AGENT);
private boolean followRedirects = true; private boolean followRedirects = true;
@ -143,48 +142,28 @@ public class HttpClient extends ContainerLifeCycle
private int requestBufferSize = 4096; private int requestBufferSize = 4096;
private int responseBufferSize = 16384; private int responseBufferSize = 16384;
private int maxRedirects = 8; private int maxRedirects = 8;
private SocketAddress bindAddress;
private long connectTimeout = 15000;
private long addressResolutionTimeout = 15000; private long addressResolutionTimeout = 15000;
private long idleTimeout;
private boolean tcpNoDelay = true; private boolean tcpNoDelay = true;
private boolean strictEventOrdering = false; private boolean strictEventOrdering = false;
private HttpField encodingField; private HttpField encodingField;
private boolean removeIdleDestinations = false; private boolean removeIdleDestinations = false;
private boolean connectBlocking = false;
private String name = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); private String name = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
private HttpCompliance httpCompliance = HttpCompliance.RFC7230; private HttpCompliance httpCompliance = HttpCompliance.RFC7230;
private String defaultRequestContentType = "application/octet-stream"; private String defaultRequestContentType = "application/octet-stream";
/** /**
* Creates a HttpClient instance that can perform requests to non-TLS destinations only * Creates a HttpClient instance that can perform HTTP/1.1 requests to non-TLS and TLS destinations.
* (that is, requests with the "http" scheme only, and not "https").
*
* @see #HttpClient(SslContextFactory) to perform requests to TLS destinations.
*/ */
public HttpClient() public HttpClient()
{ {
this(null); this(new HttpClientTransportOverHTTP());
} }
/** public HttpClient(HttpClientTransport transport)
* Creates a HttpClient instance that can perform requests to non-TLS and TLS destinations
* (that is, both requests with the "http" scheme and with the "https" scheme).
*
* @param sslContextFactory the {@link SslContextFactory} that manages TLS encryption
* @see #getSslContextFactory()
*/
public HttpClient(SslContextFactory sslContextFactory)
{ {
this(new HttpClientTransportOverHTTP(), sslContextFactory); this.transport = Objects.requireNonNull(transport);
}
public HttpClient(HttpClientTransport transport, SslContextFactory sslContextFactory)
{
this.transport = transport;
addBean(transport); addBean(transport);
this.sslContextFactory = sslContextFactory; this.connector = ((AbstractHttpClientTransport)transport).getBean(ClientConnector.class);
addBean(sslContextFactory);
addBean(handlers); addBean(handlers);
addBean(decoderFactories); addBean(decoderFactories);
} }
@ -202,34 +181,34 @@ public class HttpClient extends ContainerLifeCycle
/** /**
* @return the {@link SslContextFactory} that manages TLS encryption * @return the {@link SslContextFactory} that manages TLS encryption
* @see #HttpClient(SslContextFactory)
*/ */
public SslContextFactory getSslContextFactory() public SslContextFactory.Client getSslContextFactory()
{ {
return sslContextFactory; return connector.getSslContextFactory();
} }
@Override @Override
protected void doStart() throws Exception protected void doStart() throws Exception
{ {
Executor executor = getExecutor();
if (executor == null) if (executor == null)
{ {
QueuedThreadPool threadPool = new QueuedThreadPool(); QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName(name); threadPool.setName(name);
setExecutor(threadPool); setExecutor(threadPool);
} }
ByteBufferPool byteBufferPool = getByteBufferPool();
if (byteBufferPool == null) if (byteBufferPool == null)
setByteBufferPool(new MappedByteBufferPool(2048, setByteBufferPool(new MappedByteBufferPool(2048,
executor instanceof ThreadPool.SizedThreadPool executor instanceof ThreadPool.SizedThreadPool
? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2 ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2
: ProcessorUtils.availableProcessors() * 2)); : ProcessorUtils.availableProcessors() * 2));
Scheduler scheduler = getScheduler();
if (scheduler == null) if (scheduler == null)
setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false)); setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false));
if (resolver == null) if (resolver == null)
setSocketAddressResolver(new SocketAddressResolver.Async(executor, scheduler, getAddressResolutionTimeout())); setSocketAddressResolver(new SocketAddressResolver.Async(getExecutor(), getScheduler(), getAddressResolutionTimeout()));
handlers.put(new ContinueProtocolHandler()); handlers.put(new ContinueProtocolHandler());
handlers.put(new RedirectProtocolHandler(this)); handlers.put(new RedirectProtocolHandler(this));
@ -291,6 +270,8 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setCookieStore(CookieStore cookieStore) public void setCookieStore(CookieStore cookieStore)
{ {
if (isStarted())
throw new IllegalStateException();
this.cookieStore = Objects.requireNonNull(cookieStore); this.cookieStore = Objects.requireNonNull(cookieStore);
this.cookieManager = newCookieManager(); this.cookieManager = newCookieManager();
} }
@ -319,6 +300,8 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setAuthenticationStore(AuthenticationStore authenticationStore) public void setAuthenticationStore(AuthenticationStore authenticationStore)
{ {
if (isStarted())
throw new IllegalStateException();
this.authenticationStore = authenticationStore; this.authenticationStore = authenticationStore;
} }
@ -523,10 +506,11 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public Destination getDestination(String scheme, String host, int port) public Destination getDestination(String scheme, String host, int port)
{ {
return destinationFor(scheme, host, port); Origin origin = createOrigin(scheme, host, port);
return resolveDestination(new HttpDestination.Key(origin, null));
} }
protected HttpDestination destinationFor(String scheme, String host, int port) private Origin createOrigin(String scheme, String host, int port)
{ {
if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme) && if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme) &&
!HttpScheme.WS.is(scheme) && !HttpScheme.WSS.is(scheme)) !HttpScheme.WS.is(scheme) && !HttpScheme.WSS.is(scheme))
@ -536,13 +520,18 @@ public class HttpClient extends ContainerLifeCycle
host = host.toLowerCase(Locale.ENGLISH); host = host.toLowerCase(Locale.ENGLISH);
port = normalizePort(scheme, port); port = normalizePort(scheme, port);
Origin origin = new Origin(scheme, host, port); return new Origin(scheme, host, port);
HttpDestination destination = destinations.get(origin); }
private HttpDestination resolveDestination(HttpDestination.Key key)
{
HttpDestination destination = destinations.get(key);
if (destination == null) if (destination == null)
{ {
destination = transport.newHttpDestination(origin); destination = getTransport().newHttpDestination(key);
// Start the destination before it's published to other threads.
addManaged(destination); addManaged(destination);
HttpDestination existing = destinations.putIfAbsent(origin, destination); HttpDestination existing = destinations.putIfAbsent(key, destination);
if (existing != null) if (existing != null)
{ {
removeBean(destination); removeBean(destination);
@ -560,7 +549,7 @@ public class HttpClient extends ContainerLifeCycle
protected boolean removeDestination(HttpDestination destination) protected boolean removeDestination(HttpDestination destination)
{ {
removeBean(destination); removeBean(destination);
return destinations.remove(destination.getOrigin(), destination); return destinations.remove(destination.getKey(), destination);
} }
/** /**
@ -573,7 +562,16 @@ public class HttpClient extends ContainerLifeCycle
protected void send(final HttpRequest request, List<Response.ResponseListener> listeners) protected void send(final HttpRequest request, List<Response.ResponseListener> listeners)
{ {
HttpDestination destination = destinationFor(request.getScheme(), request.getHost(), request.getPort()); Origin origin = createOrigin(request.getScheme(), request.getHost(), request.getPort());
HttpClientTransport transport = getTransport();
HttpDestination.Key destinationKey = null;
if (transport instanceof HttpClientTransport.Dynamic)
destinationKey = ((HttpClientTransport.Dynamic)transport).newDestinationKey(request, origin);
if (destinationKey == null)
destinationKey = new HttpDestination.Key(origin, null);
if (LOG.isDebugEnabled())
LOG.debug("Selected {} for {}", destinationKey, request);
HttpDestination destination = resolveDestination(destinationKey);
destination.send(request, listeners); destination.send(request, listeners);
} }
@ -636,7 +634,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public ByteBufferPool getByteBufferPool() public ByteBufferPool getByteBufferPool()
{ {
return byteBufferPool; return connector.getByteBufferPool();
} }
/** /**
@ -644,10 +642,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setByteBufferPool(ByteBufferPool byteBufferPool) public void setByteBufferPool(ByteBufferPool byteBufferPool)
{ {
if (isStarted()) connector.setByteBufferPool(byteBufferPool);
LOG.warn("Calling setByteBufferPool() while started is deprecated");
updateBean(this.byteBufferPool, byteBufferPool);
this.byteBufferPool = byteBufferPool;
} }
/** /**
@ -677,7 +672,7 @@ public class HttpClient extends ContainerLifeCycle
@ManagedAttribute("The timeout, in milliseconds, for connect() operations") @ManagedAttribute("The timeout, in milliseconds, for connect() operations")
public long getConnectTimeout() public long getConnectTimeout()
{ {
return connectTimeout; return connector.getConnectTimeout().toMillis();
} }
/** /**
@ -686,7 +681,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setConnectTimeout(long connectTimeout) public void setConnectTimeout(long connectTimeout)
{ {
this.connectTimeout = connectTimeout; connector.setConnectTimeout(Duration.ofMillis(connectTimeout));
} }
/** /**
@ -718,7 +713,7 @@ public class HttpClient extends ContainerLifeCycle
@ManagedAttribute("The timeout, in milliseconds, to close idle connections") @ManagedAttribute("The timeout, in milliseconds, to close idle connections")
public long getIdleTimeout() public long getIdleTimeout()
{ {
return idleTimeout; return connector.getIdleTimeout().toMillis();
} }
/** /**
@ -726,7 +721,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setIdleTimeout(long idleTimeout) public void setIdleTimeout(long idleTimeout)
{ {
this.idleTimeout = idleTimeout; connector.setIdleTimeout(Duration.ofMillis(idleTimeout));
} }
/** /**
@ -735,7 +730,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public SocketAddress getBindAddress() public SocketAddress getBindAddress()
{ {
return bindAddress; return connector.getBindAddress();
} }
/** /**
@ -745,7 +740,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setBindAddress(SocketAddress bindAddress) public void setBindAddress(SocketAddress bindAddress)
{ {
this.bindAddress = bindAddress; connector.setBindAddress(bindAddress);
} }
/** /**
@ -790,7 +785,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public Executor getExecutor() public Executor getExecutor()
{ {
return executor; return connector.getExecutor();
} }
/** /**
@ -798,10 +793,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setExecutor(Executor executor) public void setExecutor(Executor executor)
{ {
if (isStarted()) connector.setExecutor(executor);
LOG.warn("Calling setExecutor() while started is deprecated");
updateBean(this.executor, executor);
this.executor = executor;
} }
/** /**
@ -809,7 +801,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public Scheduler getScheduler() public Scheduler getScheduler()
{ {
return scheduler; return connector.getScheduler();
} }
/** /**
@ -817,10 +809,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setScheduler(Scheduler scheduler) public void setScheduler(Scheduler scheduler)
{ {
if (isStarted()) connector.setScheduler(scheduler);
LOG.warn("Calling setScheduler() while started is deprecated");
updateBean(this.scheduler, scheduler);
this.scheduler = scheduler;
} }
/** /**
@ -837,7 +826,7 @@ public class HttpClient extends ContainerLifeCycle
public void setSocketAddressResolver(SocketAddressResolver resolver) public void setSocketAddressResolver(SocketAddressResolver resolver)
{ {
if (isStarted()) if (isStarted())
LOG.warn("Calling setSocketAddressResolver() while started is deprecated"); throw new IllegalStateException();
updateBean(this.resolver, resolver); updateBean(this.resolver, resolver);
this.resolver = resolver; this.resolver = resolver;
} }
@ -929,7 +918,7 @@ public class HttpClient extends ContainerLifeCycle
} }
/** /**
* @return the max number of HTTP redirects that are followed * @return the max number of HTTP redirects that are followed in a conversation
* @see #setMaxRedirects(int) * @see #setMaxRedirects(int)
*/ */
public int getMaxRedirects() public int getMaxRedirects()
@ -938,7 +927,7 @@ public class HttpClient extends ContainerLifeCycle
} }
/** /**
* @param maxRedirects the max number of HTTP redirects that are followed * @param maxRedirects the max number of HTTP redirects that are followed in a conversation, or -1 for unlimited redirects
* @see #setFollowRedirects(boolean) * @see #setFollowRedirects(boolean)
*/ */
public void setMaxRedirects(int maxRedirects) public void setMaxRedirects(int maxRedirects)
@ -1059,7 +1048,7 @@ public class HttpClient extends ContainerLifeCycle
@ManagedAttribute("Whether the connect() operation is blocking") @ManagedAttribute("Whether the connect() operation is blocking")
public boolean isConnectBlocking() public boolean isConnectBlocking()
{ {
return connectBlocking; return connector.isConnectBlocking();
} }
/** /**
@ -1074,7 +1063,7 @@ public class HttpClient extends ContainerLifeCycle
*/ */
public void setConnectBlocking(boolean connectBlocking) public void setConnectBlocking(boolean connectBlocking)
{ {
this.connectBlocking = connectBlocking; connector.setConnectBlocking(connectBlocking);
} }
/** /**
@ -1109,7 +1098,7 @@ public class HttpClient extends ContainerLifeCycle
protected String normalizeHost(String host) protected String normalizeHost(String host)
{ {
if (host != null && host.matches("\\[.*\\]")) if (host != null && host.matches("\\[.*]"))
return host.substring(1, host.length() - 1); return host.substring(1, host.length() - 1);
return host; return host;
} }

View File

@ -43,7 +43,7 @@ public interface HttpClientTransport extends ClientConnectionFactory
* Sets the {@link HttpClient} instance on this transport. * Sets the {@link HttpClient} instance on this transport.
* <p> * <p>
* This is needed because of a chicken-egg problem: in order to create the {@link HttpClient} * This is needed because of a chicken-egg problem: in order to create the {@link HttpClient}
* a {@link HttpClientTransport} is needed, that therefore cannot have a reference yet to the * a HttpClientTransport is needed, that therefore cannot have a reference yet to the
* {@link HttpClient}. * {@link HttpClient}.
* *
* @param client the {@link HttpClient} that uses this transport. * @param client the {@link HttpClient} that uses this transport.
@ -56,15 +56,15 @@ public interface HttpClientTransport extends ClientConnectionFactory
* {@link HttpDestination} controls the destination-connection cardinality: protocols like * {@link HttpDestination} controls the destination-connection cardinality: protocols like
* HTTP have 1-N cardinality, while multiplexed protocols like HTTP/2 have a 1-1 cardinality. * HTTP have 1-N cardinality, while multiplexed protocols like HTTP/2 have a 1-1 cardinality.
* *
* @param origin the destination origin * @param key the destination key
* @return a new, transport-specific, {@link HttpDestination} object * @return a new, transport-specific, {@link HttpDestination} object
*/ */
public HttpDestination newHttpDestination(Origin origin); public HttpDestination newHttpDestination(HttpDestination.Key key);
/** /**
* Establishes a physical connection to the given {@code address}. * Establishes a physical connection to the given {@code address}.
* *
* @param address the address to connect to * @param address the address to connect to
* @param context the context information to establish the connection * @param context the context information to establish the connection
*/ */
public void connect(InetSocketAddress address, Map<String, Object> context); public void connect(InetSocketAddress address, Map<String, Object> context);
@ -78,4 +78,20 @@ public interface HttpClientTransport extends ClientConnectionFactory
* @param factory the factory for ConnectionPool instances * @param factory the factory for ConnectionPool instances
*/ */
public void setConnectionPoolFactory(ConnectionPool.Factory factory); public void setConnectionPoolFactory(ConnectionPool.Factory factory);
/**
* Specifies whether a {@link HttpClientTransport} is dynamic.
*/
@FunctionalInterface
public interface Dynamic
{
/**
* Creates a new Key with the given request and origin.
*
* @param request the request that triggers the creation of the Key
* @param origin the origin of the server for the request
* @return a Key that identifies a destination
*/
public HttpDestination.Key newDestinationKey(HttpRequest request, Origin origin);
}
} }

View File

@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
@ -38,7 +37,7 @@ import org.eclipse.jetty.util.HttpCookieStore;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
public abstract class HttpConnection implements Connection public abstract class HttpConnection implements IConnection
{ {
private static final Logger LOG = Log.getLogger(HttpConnection.class); private static final Logger LOG = Log.getLogger(HttpConnection.class);
@ -80,8 +79,6 @@ public abstract class HttpConnection implements Connection
httpRequest.abort(result.failure); httpRequest.abort(result.failure);
} }
protected abstract SendFailure send(HttpExchange exchange);
protected void normalizeRequest(Request request) protected void normalizeRequest(Request request)
{ {
HttpVersion version = request.getVersion(); HttpVersion version = request.getVersion();

View File

@ -25,9 +25,13 @@ import java.util.concurrent.ConcurrentLinkedDeque;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.util.AttributesMap; import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class HttpConversation extends AttributesMap public class HttpConversation extends AttributesMap
{ {
private static final Logger LOG = Log.getLogger(HttpConversation.class);
private final Deque<HttpExchange> exchanges = new ConcurrentLinkedDeque<>(); private final Deque<HttpExchange> exchanges = new ConcurrentLinkedDeque<>();
private volatile List<Response.ResponseListener> listeners; private volatile List<Response.ResponseListener> listeners;
@ -118,6 +122,7 @@ public class HttpConversation extends AttributesMap
HttpExchange lastExchange = exchanges.peekLast(); HttpExchange lastExchange = exchanges.peekLast();
if (firstExchange == lastExchange) if (firstExchange == lastExchange)
{ {
// We don't have a conversation, just a single request.
if (overrideListener != null) if (overrideListener != null)
listeners.add(overrideListener); listeners.add(overrideListener);
else else
@ -125,13 +130,16 @@ public class HttpConversation extends AttributesMap
} }
else else
{ {
// Order is important, we want to notify the last exchange first // We have a conversation (e.g. redirect, authentication).
// Order is important, we want to notify the last exchange first.
listeners.addAll(lastExchange.getResponseListeners()); listeners.addAll(lastExchange.getResponseListeners());
if (overrideListener != null) if (overrideListener != null)
listeners.add(overrideListener); listeners.add(overrideListener);
else else
listeners.addAll(firstExchange.getResponseListeners()); listeners.addAll(firstExchange.getResponseListeners());
} }
if (LOG.isDebugEnabled())
LOG.debug("Exchanges in conversation {}, override={}, listeners={}", exchanges.size(), overrideListener, listeners);
this.listeners = listeners; this.listeners = listeners;
} }

View File

@ -23,20 +23,25 @@ import java.io.IOException;
import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousCloseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.CyclicTimeout; import org.eclipse.jetty.io.CyclicTimeout;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.HostPort;
@ -52,12 +57,12 @@ import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.Sweeper; import org.eclipse.jetty.util.thread.Sweeper;
@ManagedObject @ManagedObject
public abstract class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Callback, Dumpable public class HttpDestination extends ContainerLifeCycle implements Destination, Closeable, Callback, Dumpable
{ {
protected static final Logger LOG = Log.getLogger(HttpDestination.class); protected static final Logger LOG = Log.getLogger(HttpDestination.class);
private final HttpClient client; private final HttpClient client;
private final Origin origin; private final Key key;
private final Queue<HttpExchange> exchanges; private final Queue<HttpExchange> exchanges;
private final RequestNotifier requestNotifier; private final RequestNotifier requestNotifier;
private final ResponseNotifier responseNotifier; private final ResponseNotifier responseNotifier;
@ -67,21 +72,38 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
private final TimeoutTask timeout; private final TimeoutTask timeout;
private ConnectionPool connectionPool; private ConnectionPool connectionPool;
public HttpDestination(HttpClient client, Origin origin) public HttpDestination(HttpClient client, Key key)
{
this(client, key, Function.identity());
}
public HttpDestination(HttpClient client, Key key, Function<ClientConnectionFactory, ClientConnectionFactory> factoryFn)
{ {
this.client = client; this.client = client;
this.origin = origin; this.key = key;
this.exchanges = newExchangeQueue(client); this.exchanges = newExchangeQueue(client);
this.requestNotifier = new RequestNotifier(client); this.requestNotifier = new RequestNotifier(client);
this.responseNotifier = new ResponseNotifier(); this.responseNotifier = new ResponseNotifier();
this.timeout = new TimeoutTask(client.getScheduler()); this.timeout = new TimeoutTask(client.getScheduler());
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
ProxyConfiguration proxyConfig = client.getProxyConfiguration(); ProxyConfiguration proxyConfig = client.getProxyConfiguration();
proxy = proxyConfig.match(origin); this.proxy = proxyConfig.match(getOrigin());
ClientConnectionFactory connectionFactory = client.getTransport();
this.connectionFactory = factoryFn.apply(createClientConnectionFactory());
}
private ClientConnectionFactory createClientConnectionFactory()
{
ProxyConfiguration.Proxy proxy = getProxy();
ClientConnectionFactory connectionFactory = getHttpClient().getTransport();
if (proxy != null) if (proxy != null)
{ {
connectionFactory = proxy.newClientConnectionFactory(connectionFactory); connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
@ -93,12 +115,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (isSecure()) if (isSecure())
connectionFactory = newSslClientConnectionFactory(connectionFactory); connectionFactory = newSslClientConnectionFactory(connectionFactory);
} }
this.connectionFactory = connectionFactory; return connectionFactory;
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
} }
@Override @Override
@ -147,9 +164,14 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
return client; return client;
} }
public Key getKey()
{
return key;
}
public Origin getOrigin() public Origin getOrigin()
{ {
return origin; return key.origin;
} }
public Queue<HttpExchange> getHttpExchanges() public Queue<HttpExchange> getHttpExchanges()
@ -181,7 +203,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
@ManagedAttribute(value = "The destination scheme", readonly = true) @ManagedAttribute(value = "The destination scheme", readonly = true)
public String getScheme() public String getScheme()
{ {
return origin.getScheme(); return getOrigin().getScheme();
} }
@Override @Override
@ -190,14 +212,14 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
{ {
// InetSocketAddress.getHostString() transforms the host string // InetSocketAddress.getHostString() transforms the host string
// in case of IPv6 addresses, so we return the original host string // in case of IPv6 addresses, so we return the original host string
return origin.getAddress().getHost(); return getOrigin().getAddress().getHost();
} }
@Override @Override
@ManagedAttribute(value = "The destination port", readonly = true) @ManagedAttribute(value = "The destination port", readonly = true)
public int getPort() public int getPort()
{ {
return origin.getAddress().getPort(); return getOrigin().getAddress().getPort();
} }
@ManagedAttribute(value = "The number of queued requests", readonly = true) @ManagedAttribute(value = "The number of queued requests", readonly = true)
@ -208,7 +230,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
public Origin.Address getConnectAddress() public Origin.Address getConnectAddress()
{ {
return proxy == null ? origin.getAddress() : proxy.getAddress(); return proxy == null ? getOrigin().getAddress() : proxy.getAddress();
} }
public HttpField getHostField() public HttpField getHostField()
@ -235,7 +257,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
} }
protected void send(HttpRequest request, List<Response.ResponseListener> listeners) protected void send(HttpRequest request, List<Response.ResponseListener> listeners)
{ {
if (!getScheme().equalsIgnoreCase(request.getScheme())) if (!getScheme().equalsIgnoreCase(request.getScheme()))
throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this); throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this);
if (!getHost().equalsIgnoreCase(request.getHost())) if (!getHost().equalsIgnoreCase(request.getHost()))
@ -343,7 +365,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
} }
else else
{ {
SendFailure result = send(connection, exchange); SendFailure result = ((IConnection)connection).send(exchange);
if (result != null) if (result != null)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -358,8 +380,6 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
} }
} }
protected abstract SendFailure send(Connection connection, HttpExchange exchange);
@Override @Override
public void newConnection(Promise<Connection> promise) public void newConnection(Promise<Connection> promise)
{ {
@ -476,7 +496,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
public String asString() public String asString()
{ {
return origin.asString(); return getKey().asString();
} }
@Override @Override
@ -490,8 +510,178 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
exchanges.size(), exchanges.size(),
connectionPool); connectionPool);
} }
// The TimeoutTask that expires when the next check of expiry is needed @FunctionalInterface
public interface Multiplexed
{
void setMaxRequestsPerConnection(int maxRequestsPerConnection);
}
/**
* <p>Class that groups the elements that uniquely identify a destination.</p>
* <p>The elements are an {@link Origin}, a {@link Protocol} and an opaque
* string that further distinguishes destinations that have the same origin
* and protocol.</p>
* <p>In general it is possible that, for the same origin, the server can
* speak different protocols (for example, clear-text HTTP/1.1 and clear-text
* HTTP/2), so the {@link Protocol} makes that distinction.</p>
* <p>Furthermore, it may be desirable to have different destinations for
* the same origin and protocol (for example, when using the PROXY protocol
* in a reverse proxy server, you want to be able to map the client ip:port
* to the destination {@code kind}, so that all the connections to the server
* associated to that destination can specify the PROXY protocol bytes for
* that particular client connection.</p>
*/
public static class Key
{
private final Origin origin;
private final Protocol protocol;
private final String kind;
/**
* Creates a Key with the given origin and protocol and a {@code null} kind.
*
* @param origin the origin
* @param protocol the protocol
*/
public Key(Origin origin, Protocol protocol)
{
this(origin, protocol, null);
}
/**
* Creates a Key with the given origin and protocol and kind.
*
* @param origin the origin
* @param protocol the protocol
* @param kind the opaque kind
*/
public Key(Origin origin, Protocol protocol, String kind)
{
this.origin = origin;
this.protocol = protocol;
this.kind = kind;
}
public Origin getOrigin()
{
return origin;
}
public Protocol getProtocol()
{
return protocol;
}
public String getKind()
{
return kind;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Key that = (Key)obj;
return origin.equals(that.origin) &&
Objects.equals(protocol, that.protocol) &&
Objects.equals(kind, that.kind);
}
@Override
public int hashCode()
{
return Objects.hash(origin, protocol, kind);
}
public String asString()
{
return String.format("%s|%s,kind=%s",
origin.asString(),
protocol == null ? "null" : protocol.asString(),
kind);
}
@Override
public String toString()
{
return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), asString());
}
}
/**
* <p>The representation of a network protocol.</p>
* <p>A network protocol may have multiple protocol <em>names</em>
* associated to it, for example {@code ["h2", "h2-17", "h2-16"]}.</p>
* <p>A Protocol is then rendered into a {@link ClientConnectionFactory}
* chain, for example in
* {@link HttpClientTransportDynamic#newConnection(EndPoint, Map)}.</p>
*/
public static class Protocol
{
private final List<String> protocols;
private final boolean negotiate;
/**
* Creates a Protocol with the given list of protocol names
* and whether it should negotiate the protocol.
*
* @param protocols the protocol names
* @param negotiate whether the protocol should be negotiated
*/
public Protocol(List<String> protocols, boolean negotiate)
{
this.protocols = protocols;
this.negotiate = negotiate;
}
public List<String> getProtocols()
{
return protocols;
}
public boolean isNegotiate()
{
return negotiate;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Protocol that = (Protocol)obj;
return protocols.equals(that.protocols) && negotiate == that.negotiate;
}
@Override
public int hashCode()
{
return Objects.hash(protocols, negotiate);
}
public String asString()
{
return String.format("proto=%s,nego=%b", protocols, negotiate);
}
@Override
public String toString()
{
return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), asString());
}
}
/**
* This class enforces the total timeout for exchanges that are still in the queue.
* The total timeout for exchanges that are not in the destination queue is enforced
* by {@link HttpChannel}.
*/
private class TimeoutTask extends CyclicTimeout private class TimeoutTask extends CyclicTimeout
{ {
private final AtomicLong nextTimeout = new AtomicLong(Long.MAX_VALUE); private final AtomicLong nextTimeout = new AtomicLong(Long.MAX_VALUE);
@ -504,10 +694,13 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
@Override @Override
public void onTimeoutExpired() public void onTimeoutExpired()
{ {
if (LOG.isDebugEnabled())
LOG.debug("{} timeout expired", this);
nextTimeout.set(Long.MAX_VALUE); nextTimeout.set(Long.MAX_VALUE);
long now = System.nanoTime(); long now = System.nanoTime();
long nextExpiresAt = Long.MAX_VALUE; long nextExpiresAt = Long.MAX_VALUE;
// Check all queued exchanges for those that have expired // Check all queued exchanges for those that have expired
// and to determine when the next check must be. // and to determine when the next check must be.
for (HttpExchange exchange : exchanges) for (HttpExchange exchange : exchanges)
@ -521,7 +714,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
else if (expiresAt < nextExpiresAt) else if (expiresAt < nextExpiresAt)
nextExpiresAt = expiresAt; nextExpiresAt = expiresAt;
} }
if (nextExpiresAt < Long.MAX_VALUE && client.isRunning()) if (nextExpiresAt < Long.MAX_VALUE && client.isRunning())
schedule(nextExpiresAt); schedule(nextExpiresAt);
} }
@ -536,12 +729,16 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (timeoutAt != expiresAt) if (timeoutAt != expiresAt)
{ {
long delay = expiresAt - System.nanoTime(); long delay = expiresAt - System.nanoTime();
if (LOG.isDebugEnabled())
LOG.debug("Scheduled timeout in {} ms", TimeUnit.NANOSECONDS.toMillis(delay));
if (delay <= 0) if (delay <= 0)
{
onTimeoutExpired(); onTimeoutExpired();
}
else else
{
schedule(delay, TimeUnit.NANOSECONDS); schedule(delay, TimeUnit.NANOSECONDS);
if (LOG.isDebugEnabled())
LOG.debug("{} scheduled timeout in {} ms", this, TimeUnit.NANOSECONDS.toMillis(delay));
}
} }
} }
} }

View File

@ -23,6 +23,7 @@ import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -61,10 +62,10 @@ public class HttpRedirector
{ {
private static final Logger LOG = Log.getLogger(HttpRedirector.class); private static final Logger LOG = Log.getLogger(HttpRedirector.class);
private static final String SCHEME_REGEXP = "(^https?)"; private static final String SCHEME_REGEXP = "(^https?)";
private static final String AUTHORITY_REGEXP = "([^/\\?#]+)"; private static final String AUTHORITY_REGEXP = "([^/?#]+)";
// The location may be relative so the scheme://authority part may be missing // The location may be relative so the scheme://authority part may be missing
private static final String DESTINATION_REGEXP = "(" + SCHEME_REGEXP + "://" + AUTHORITY_REGEXP + ")?"; private static final String DESTINATION_REGEXP = "(" + SCHEME_REGEXP + "://" + AUTHORITY_REGEXP + ")?";
private static final String PATH_REGEXP = "([^\\?#]*)"; private static final String PATH_REGEXP = "([^?#]*)";
private static final String QUERY_REGEXP = "([^#]*)"; private static final String QUERY_REGEXP = "([^#]*)";
private static final String FRAGMENT_REGEXP = "(.*)"; private static final String FRAGMENT_REGEXP = "(.*)";
private static final Pattern URI_PATTERN = Pattern.compile(DESTINATION_REGEXP + PATH_REGEXP + QUERY_REGEXP + FRAGMENT_REGEXP); private static final Pattern URI_PATTERN = Pattern.compile(DESTINATION_REGEXP + PATH_REGEXP + QUERY_REGEXP + FRAGMENT_REGEXP);
@ -101,11 +102,11 @@ public class HttpRedirector
/** /**
* Redirects the given {@code response}, blocking until the redirect is complete. * Redirects the given {@code response}, blocking until the redirect is complete.
* *
* @param request the original request that triggered the redirect * @param request the original request that triggered the redirect
* @param response the response to the original request * @param response the response to the original request
* @return a {@link Result} object containing the request to the redirected location and its response * @return a {@link Result} object containing the request to the redirected location and its response
* @throws InterruptedException if the thread is interrupted while waiting for the redirect to complete * @throws InterruptedException if the thread is interrupted while waiting for the redirect to complete
* @throws ExecutionException if the redirect failed * @throws ExecutionException if the redirect failed
* @see #redirect(Request, Response, Response.CompleteListener) * @see #redirect(Request, Response, Response.CompleteListener)
*/ */
public Result redirect(Request request, Response response) throws InterruptedException, ExecutionException public Result redirect(Request request, Response response) throws InterruptedException, ExecutionException
@ -144,7 +145,7 @@ public class HttpRedirector
/** /**
* Redirects the given {@code response} asynchronously. * Redirects the given {@code response} asynchronously.
* *
* @param request the original request that triggered the redirect * @param request the original request that triggered the redirect
* @param response the response to the original request * @param response the response to the original request
* @param listener the listener that receives response events * @param listener the listener that receives response events
* @return the request to the redirected location * @return the request to the redirected location
@ -292,7 +293,8 @@ public class HttpRedirector
Integer redirects = (Integer)conversation.getAttribute(ATTRIBUTE); Integer redirects = (Integer)conversation.getAttribute(ATTRIBUTE);
if (redirects == null) if (redirects == null)
redirects = 0; redirects = 0;
if (redirects < client.getMaxRedirects()) int maxRedirects = client.getMaxRedirects();
if (maxRedirects < 0 || redirects < maxRedirects)
{ {
++redirects; ++redirects;
conversation.setAttribute(ATTRIBUTE, redirects); conversation.setAttribute(ATTRIBUTE, redirects);
@ -310,19 +312,17 @@ public class HttpRedirector
try try
{ {
Request redirect = client.copyRequest(httpRequest, location); Request redirect = client.copyRequest(httpRequest, location);
// Disable the timeout so that only the one from the initial request applies.
redirect.timeout(0, TimeUnit.MILLISECONDS);
// Use given method // Use given method
redirect.method(method); redirect.method(method);
redirect.onRequestBegin(new Request.BeginListener() redirect.onRequestBegin(request ->
{ {
@Override Throwable cause = httpRequest.getAbortCause();
public void onBegin(Request redirect) if (cause != null)
{ request.abort(cause);
Throwable cause = httpRequest.getAbortCause();
if (cause != null)
redirect.abort(cause);
}
}); });
redirect.send(listener); redirect.send(listener);

View File

@ -18,10 +18,9 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
public abstract class PoolingHttpDestination extends HttpDestination import org.eclipse.jetty.client.api.Connection;
public interface IConnection extends Connection
{ {
public PoolingHttpDestination(HttpClient client, Origin origin) public SendFailure send(HttpExchange exchange);
{
super(client, origin);
}
} }

View File

@ -22,11 +22,10 @@ import java.io.IOException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
@ -41,11 +40,9 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
{ {
private static final Logger LOG = Log.getLogger(MultiplexConnectionPool.class); private static final Logger LOG = Log.getLogger(MultiplexConnectionPool.class);
private final ReentrantLock lock = new ReentrantLock();
private final HttpDestination destination; private final HttpDestination destination;
private final Deque<Holder> idleConnections; private final Deque<Holder> idleConnections;
private final Map<Connection, Holder> muxedConnections; private final Map<Connection, Holder> activeConnections;
private final Map<Connection, Holder> busyConnections;
private int maxMultiplex; private int maxMultiplex;
public MultiplexConnectionPool(HttpDestination destination, int maxConnections, Callback requester, int maxMultiplex) public MultiplexConnectionPool(HttpDestination destination, int maxConnections, Callback requester, int maxMultiplex)
@ -53,8 +50,7 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
super(destination, maxConnections, requester); super(destination, maxConnections, requester);
this.destination = destination; this.destination = destination;
this.idleConnections = new ArrayDeque<>(maxConnections); this.idleConnections = new ArrayDeque<>(maxConnections);
this.muxedConnections = new HashMap<>(maxConnections); this.activeConnections = new LinkedHashMap<>(maxConnections);
this.busyConnections = new HashMap<>(maxConnections);
this.maxMultiplex = maxMultiplex; this.maxMultiplex = maxMultiplex;
} }
@ -69,120 +65,73 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
connection = activate(); connection = activate();
} }
return connection; return connection;
}
protected void lock()
{
lock.lock();
}
protected void unlock()
{
lock.unlock();
} }
@Override @Override
public int getMaxMultiplex() public int getMaxMultiplex()
{ {
lock(); synchronized (this)
try
{ {
return maxMultiplex; return maxMultiplex;
} }
finally
{
unlock();
}
} }
@Override @Override
public void setMaxMultiplex(int maxMultiplex) public void setMaxMultiplex(int maxMultiplex)
{ {
lock(); synchronized (this)
try
{ {
this.maxMultiplex = maxMultiplex; this.maxMultiplex = maxMultiplex;
} }
finally
{
unlock();
}
} }
@Override @Override
public boolean isActive(Connection connection) public boolean isActive(Connection connection)
{ {
lock(); synchronized (this)
try
{ {
if (muxedConnections.containsKey(connection)) return activeConnections.containsKey(connection);
return true;
if (busyConnections.containsKey(connection))
return true;
return false;
}
finally
{
unlock();
} }
} }
@Override @Override
protected void onCreated(Connection connection) protected void onCreated(Connection connection)
{ {
lock(); synchronized (this)
try
{ {
// Use "cold" connections as last. // Use "cold" connections as last.
idleConnections.offer(new Holder(connection)); idleConnections.offer(new Holder(connection));
} }
finally
{
unlock();
}
idle(connection, false); idle(connection, false);
} }
@Override @Override
protected Connection activate() protected Connection activate()
{ {
Holder holder; Holder result = null;
lock(); synchronized (this)
try
{ {
while (true) for (Holder holder : activeConnections.values())
{ {
if (muxedConnections.isEmpty())
{
holder = idleConnections.poll();
if (holder == null)
return null;
muxedConnections.put(holder.connection, holder);
}
else
{
holder = muxedConnections.values().iterator().next();
}
if (holder.count < maxMultiplex) if (holder.count < maxMultiplex)
{ {
++holder.count; result = holder;
break; break;
} }
else
{
muxedConnections.remove(holder.connection);
busyConnections.put(holder.connection, holder);
}
} }
}
finally
{
unlock();
}
return active(holder.connection); if (result == null)
{
Holder holder = idleConnections.poll();
if (holder == null)
return null;
activeConnections.put(holder.connection, holder);
result = holder;
}
++result.count;
}
return active(result.connection);
} }
@Override @Override
@ -191,16 +140,15 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
boolean closed = isClosed(); boolean closed = isClosed();
boolean idle = false; boolean idle = false;
Holder holder; Holder holder;
lock(); synchronized (this)
try
{ {
holder = muxedConnections.get(connection); holder = activeConnections.get(connection);
if (holder != null) if (holder != null)
{ {
int count = --holder.count; int count = --holder.count;
if (count == 0) if (count == 0)
{ {
muxedConnections.remove(connection); activeConnections.remove(connection);
if (!closed) if (!closed)
{ {
idleConnections.offerFirst(holder); idleConnections.offerFirst(holder);
@ -208,32 +156,7 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
} }
} }
} }
else
{
holder = busyConnections.remove(connection);
if (holder != null)
{
int count = --holder.count;
if (!closed)
{
if (count == 0)
{
idleConnections.offerFirst(holder);
idle = true;
}
else
{
muxedConnections.put(connection, holder);
}
}
}
}
} }
finally
{
unlock();
}
if (holder == null) if (holder == null)
return false; return false;
@ -253,16 +176,13 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
{ {
boolean activeRemoved = true; boolean activeRemoved = true;
boolean idleRemoved = false; boolean idleRemoved = false;
lock(); synchronized (this)
try
{ {
Holder holder = muxedConnections.remove(connection); Holder holder = activeConnections.remove(connection);
if (holder == null)
holder = busyConnections.remove(connection);
if (holder == null) if (holder == null)
{ {
activeRemoved = false; activeRemoved = false;
for (Iterator<Holder> iterator = idleConnections.iterator(); iterator.hasNext();) for (Iterator<Holder> iterator = idleConnections.iterator(); iterator.hasNext(); )
{ {
holder = iterator.next(); holder = iterator.next();
if (holder.connection == connection) if (holder.connection == connection)
@ -274,11 +194,6 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
} }
} }
} }
finally
{
unlock();
}
if (activeRemoved || force) if (activeRemoved || force)
released(connection); released(connection);
boolean removed = activeRemoved || idleRemoved || force; boolean removed = activeRemoved || idleRemoved || force;
@ -291,65 +206,39 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
public void close() public void close()
{ {
super.close(); super.close();
List<Connection> connections; List<Connection> connections;
lock(); synchronized (this)
try
{ {
connections = idleConnections.stream().map(holder -> holder.connection).collect(Collectors.toList()); connections = idleConnections.stream().map(holder -> holder.connection).collect(Collectors.toList());
connections.addAll(muxedConnections.keySet()); connections.addAll(activeConnections.keySet());
connections.addAll(busyConnections.keySet());
} }
finally
{
unlock();
}
close(connections); close(connections);
} }
@Override @Override
public void dump(Appendable out, String indent) throws IOException public void dump(Appendable out, String indent) throws IOException
{ {
DumpableCollection busy; DumpableCollection active;
DumpableCollection muxed;
DumpableCollection idle; DumpableCollection idle;
lock(); synchronized (this)
try
{ {
busy = new DumpableCollection("busy", new ArrayList<>(busyConnections.values())); active = new DumpableCollection("active", new ArrayList<>(activeConnections.values()));
muxed = new DumpableCollection("muxed", new ArrayList<>(muxedConnections.values()));
idle = new DumpableCollection("idle", new ArrayList<>(idleConnections)); idle = new DumpableCollection("idle", new ArrayList<>(idleConnections));
} }
finally Dumpable.dumpObjects(out, indent, this, active, idle);
{
unlock();
}
Dumpable.dumpObjects(out, indent, this, busy, muxed, idle);
} }
@Override @Override
public boolean sweep() public boolean sweep()
{ {
List<Connection> toSweep = new ArrayList<>(); List<Connection> toSweep = new ArrayList<>();
lock(); synchronized (this)
try
{ {
busyConnections.values().stream() activeConnections.values().stream()
.map(holder -> holder.connection)
.filter(connection -> connection instanceof Sweeper.Sweepable)
.collect(Collectors.toCollection(() -> toSweep));
muxedConnections.values().stream()
.map(holder -> holder.connection) .map(holder -> holder.connection)
.filter(connection -> connection instanceof Sweeper.Sweepable) .filter(connection -> connection instanceof Sweeper.Sweepable)
.collect(Collectors.toCollection(() -> toSweep)); .collect(Collectors.toCollection(() -> toSweep));
} }
finally
{
unlock();
}
for (Connection connection : toSweep) for (Connection connection : toSweep)
{ {
if (((Sweeper.Sweepable)connection).sweep()) if (((Sweeper.Sweepable)connection).sweep())
@ -363,34 +252,26 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
dump()); dump());
} }
} }
return false; return false;
} }
@Override @Override
public String toString() public String toString()
{ {
int busySize; int activeSize;
int muxedSize;
int idleSize; int idleSize;
lock(); synchronized (this)
try
{ {
busySize = busyConnections.size(); activeSize = activeConnections.size();
muxedSize = muxedConnections.size();
idleSize = idleConnections.size(); idleSize = idleConnections.size();
} }
finally return String.format("%s@%x[connections=%d/%d,multiplex=%d,active=%d,idle=%d]",
{
unlock();
}
return String.format("%s@%x[c=%d/%d,b=%d,m=%d,i=%d]",
getClass().getSimpleName(), getClass().getSimpleName(),
hashCode(), hashCode(),
getConnectionCount(), getConnectionCount(),
getMaxConnectionCount(), getMaxConnectionCount(),
busySize, getMaxMultiplex(),
muxedSize, activeSize,
idleSize); idleSize);
} }

View File

@ -18,13 +18,33 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
public abstract class MultiplexHttpDestination extends HttpDestination import java.util.function.Function;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
/**
* <p>A destination for those transports that are multiplex (e.g. HTTP/2).</p>
* <p>Transports that negotiate the protocol, and therefore do not know in advance
* whether they are duplex or multiplex, should use this class and when the
* cardinality is known call {@link #setMaxRequestsPerConnection(int)} with
* the proper cardinality.</p>
* <p>If the cardinality is {@code 1}, the behavior of this class is similar
* to that of {@link DuplexHttpDestination}.</p>
*/
public class MultiplexHttpDestination extends HttpDestination implements HttpDestination.Multiplexed
{ {
protected MultiplexHttpDestination(HttpClient client, Origin origin) public MultiplexHttpDestination(HttpClient client, Key key)
{ {
super(client, origin); this(client, key, Function.identity());
} }
public MultiplexHttpDestination(HttpClient client, Key key, Function<ClientConnectionFactory, ClientConnectionFactory> factoryFn)
{
super(client, key, factoryFn);
}
@ManagedAttribute(value = "The maximum number of concurrent requests per connection")
public int getMaxRequestsPerConnection() public int getMaxRequestsPerConnection()
{ {
ConnectionPool connectionPool = getConnectionPool(); ConnectionPool connectionPool = getConnectionPool();

View File

@ -75,7 +75,7 @@ public class Origin
@Override @Override
public String toString() public String toString()
{ {
return asString(); return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), asString());
} }
public static class Address public static class Address

View File

@ -73,7 +73,7 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
@Override @Override
protected URI getAuthenticationURI(Request request) protected URI getAuthenticationURI(Request request)
{ {
HttpDestination destination = getHttpClient().destinationFor(request.getScheme(), request.getHost(), request.getPort()); HttpDestination destination = (HttpDestination)getHttpClient().getDestination(request.getScheme(), request.getHost(), request.getPort());
ProxyConfiguration.Proxy proxy = destination.getProxy(); ProxyConfiguration.Proxy proxy = destination.getProxy();
return proxy != null ? proxy.getURI() : request.getURI(); return proxy != null ? proxy.getURI() : request.getURI();
} }

View File

@ -26,7 +26,6 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.io.CyclicTimeout; import org.eclipse.jetty.io.CyclicTimeout;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.Scheduler;
@ -47,7 +46,7 @@ public class TimeoutCompleteListener extends CyclicTimeout implements Response.C
{ {
Request request = this.request.getAndSet(null); Request request = this.request.getAndSet(null);
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Total timeout {} ms elapsed for {}", request.getTimeout(), request); LOG.debug("Total timeout {} ms elapsed for {} on {}", request.getTimeout(), request, this);
if (request != null) if (request != null)
request.abort(new TimeoutException("Total timeout " + request.getTimeout() + " ms elapsed")); request.abort(new TimeoutException("Total timeout " + request.getTimeout() + " ms elapsed"));
} }
@ -60,7 +59,7 @@ public class TimeoutCompleteListener extends CyclicTimeout implements Response.C
{ {
boolean cancelled = cancel(); boolean cancelled = cancel();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Cancelled ({}) timeout for {}", cancelled, request); LOG.debug("Cancelled ({}) timeout for {} on {}", cancelled, request, this);
} }
} }
@ -69,12 +68,16 @@ public class TimeoutCompleteListener extends CyclicTimeout implements Response.C
if (this.request.compareAndSet(null, request)) if (this.request.compareAndSet(null, request))
{ {
long delay = timeoutAt - System.nanoTime(); long delay = timeoutAt - System.nanoTime();
if (LOG.isDebugEnabled())
LOG.debug("Scheduled timeout in {} ms for {}", TimeUnit.NANOSECONDS.toMillis(delay), request);
if (delay <= 0) if (delay <= 0)
{
onTimeoutExpired(); onTimeoutExpired();
}
else else
{
schedule(delay, TimeUnit.NANOSECONDS); schedule(delay, TimeUnit.NANOSECONDS);
if (LOG.isDebugEnabled())
LOG.debug("Scheduled timeout in {} ms for {} on {}", TimeUnit.NANOSECONDS.toMillis(delay), request, this);
}
} }
} }
} }

View File

@ -261,12 +261,14 @@ public interface Request
Request idleTimeout(long timeout, TimeUnit unit); Request idleTimeout(long timeout, TimeUnit unit);
/** /**
* @return the total timeout for this request, in milliseconds * @return the total timeout for this request, in milliseconds;
* zero or negative if the timeout is disabled
*/ */
long getTimeout(); long getTimeout();
/** /**
* @param timeout the total timeout for the request/response conversation * @param timeout the total timeout for the request/response conversation;
* use zero or a negative value to disable the timeout
* @param unit the timeout unit * @param unit the timeout unit
* @return this request object * @return this request object
*/ */

View File

@ -0,0 +1,201 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client.dynamic;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
import org.eclipse.jetty.client.AbstractConnectorHttpClientTransport;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.MultiplexConnectionPool;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
/**
* <p>A {@link HttpClientTransport} that can dynamically switch among different application protocols.</p>
* <p>Applications create HttpClientTransportDynamic instances specifying all the <em>application protocols</em>
* it supports, in order of preference. The typical case is when the server supports both HTTP/1.1 and
* HTTP/2, but the client does not know that. In this case, the application will create a
* HttpClientTransportDynamic in this way:</p>
* <pre>
* ClientConnector clientConnector = new ClientConnector();
* // Configure the clientConnector.
*
* // Prepare the application protocols.
* HttpClientConnectionFactory.Key h1 = HttpClientConnectionFactory.HTTP;
* HTTP2Client http2Client = new HTTP2Client(clientConnector);
* ClientConnectionFactory.Key h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
*
* // Create the HttpClientTransportDynamic, preferring h2 over h1.
* HttpClientTransport transport = new HttpClientTransportDynamic(clientConnector, h2, h1);
*
* // Create the HttpClient.
* client = new HttpClient(transport);
* </pre>
* <p>Note how in the code above the HttpClientTransportDynamic has been created with the <em>application
* protocols</em> {@code h2} and {@code h1}, without the need to specify TLS (which is implied by the request
* scheme) or ALPN (which is implied by HTTP/2 over TLS).</p>
* <p>When a request is first sent, a destination needs to be created, and the {@link org.eclipse.jetty.client.Origin}
* {@code (scheme, host, port)} is not enough to identify the destination because the same origin may speak
* different protocols.
* For example, the Jetty server supports speaking clear-text {@code http/1.1} and {@code h2c} on the same port.
* Imagine a client sending a {@code h2c} request to that port; this will create a destination and connections
* that speak {@code h2c}; it won't be possible to use the connections from that destination to send
* {@code http/1.1} requests.
* Therefore a destination is identified by a {@link org.eclipse.jetty.client.HttpDestination.Key} and
* applications can customize the creation of the destination key (for example depending on request protocol
* version, or request headers, or request attributes, or even request path) by overriding
* {@link #newDestinationKey(HttpRequest, Origin)}.</p>
*/
public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTransport implements HttpClientTransport.Dynamic
{
private final List<ClientConnectionFactory.Info> factoryInfos;
private final List<String> protocols;
/**
* Creates a transport that speaks only HTTP/1.1.
*/
public HttpClientTransportDynamic()
{
this(new ClientConnector(), HttpClientConnectionFactory.HTTP11);
}
/**
* Creates a transport with the given {@link ClientConnector} and the given <em>application protocols</em>.
*
* @param connector the ClientConnector used by this transport
* @param factoryInfos the <em>application protocols</em> that this transport can speak
*/
public HttpClientTransportDynamic(ClientConnector connector, ClientConnectionFactory.Info... factoryInfos)
{
super(connector);
addBean(connector);
if (factoryInfos.length == 0)
throw new IllegalArgumentException("Missing ClientConnectionFactory");
this.factoryInfos = Arrays.asList(factoryInfos);
this.protocols = Arrays.stream(factoryInfos)
.flatMap(info -> info.getProtocols().stream())
.distinct()
.collect(Collectors.toList());
for (ClientConnectionFactory.Info factoryInfo : factoryInfos)
addBean(factoryInfo);
setConnectionPoolFactory(destination ->
new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1));
}
@Override
public HttpDestination.Key newDestinationKey(HttpRequest request, Origin origin)
{
boolean ssl = HttpScheme.HTTPS.is(request.getScheme());
String http2 = ssl ? "h2" : "h2c";
List<String> protocols = List.of();
if (request.getVersion() == HttpVersion.HTTP_2)
{
// The application is explicitly asking for HTTP/2, so exclude HTTP/1.1.
if (this.protocols.contains(http2))
protocols = List.of(http2);
}
else
{
// Preserve the order of protocols chosen by the application.
protocols = this.protocols.stream()
.filter(p -> p.equals("http/1.1") || p.equals(http2))
.collect(Collectors.toList());
}
if (protocols.isEmpty())
return new HttpDestination.Key(origin, null);
return new HttpDestination.Key(origin, new HttpDestination.Protocol(protocols, ssl));
}
@Override
public HttpDestination newHttpDestination(HttpDestination.Key key)
{
return new MultiplexHttpDestination(getHttpClient(), key);
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
HttpDestination.Protocol protocol = destination.getKey().getProtocol();
ClientConnectionFactory.Info factoryInfo;
if (protocol == null)
{
// Use the default ClientConnectionFactory.
factoryInfo = factoryInfos.get(0);
}
else
{
if (destination.isSecure() && protocol.isNegotiate())
{
factoryInfo = new ALPNClientConnectionFactory.ALPN(getClientConnector().getExecutor(), this::newNegotiatedConnection, protocol.getProtocols());
}
else
{
factoryInfo = findClientConnectionFactoryInfo(protocol.getProtocols())
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol));
}
}
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
}
protected Connection newNegotiatedConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
try
{
ALPNClientConnection alpnConnection = (ALPNClientConnection)endPoint.getConnection();
String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled())
LOG.debug("ALPN negotiated {} among {}", protocol, alpnConnection.getProtocols());
if (protocol == null)
throw new IOException("Could not negotiate protocol among " + alpnConnection.getProtocols());
List<String> protocols = List.of(protocol);
Info factoryInfo = findClientConnectionFactoryInfo(protocols)
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for negotiated protocol " + protocol));
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
}
catch (Throwable failure)
{
this.connectFailed(context, failure);
throw failure;
}
}
private Optional<Info> findClientConnectionFactoryInfo(List<String> protocols)
{
return factoryInfos.stream()
.filter(info -> info.matches(protocols))
.findFirst();
}
}

View File

@ -0,0 +1,43 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client.http;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Promise;
public class HttpClientConnectionFactory implements ClientConnectionFactory
{
public static final Info HTTP11 = new Info(List.of("http/1.1"), new HttpClientConnectionFactory());
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context)
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
return customize(new HttpConnectionOverHTTP(endPoint, destination, promise), context);
}
}

View File

@ -23,8 +23,8 @@ import java.util.Map;
import org.eclipse.jetty.client.AbstractConnectorHttpClientTransport; import org.eclipse.jetty.client.AbstractConnectorHttpClientTransport;
import org.eclipse.jetty.client.DuplexConnectionPool; import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
@ -53,9 +53,9 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
} }
@Override @Override
public HttpDestination newHttpDestination(Origin origin) public HttpDestination newHttpDestination(HttpDestination.Key key)
{ {
return new HttpDestinationOverHTTP(getHttpClient(), origin); return new DuplexHttpDestination(getHttpClient(), key);
} }
@Override @Override

View File

@ -28,6 +28,7 @@ import java.util.concurrent.atomic.LongAdder;
import org.eclipse.jetty.client.HttpConnection; import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.IConnection;
import org.eclipse.jetty.client.SendFailure; import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
@ -39,7 +40,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Sweeper; import org.eclipse.jetty.util.thread.Sweeper;
public class HttpConnectionOverHTTP extends AbstractConnection implements Connection, org.eclipse.jetty.io.Connection.UpgradeFrom, Sweeper.Sweepable public class HttpConnectionOverHTTP extends AbstractConnection implements IConnection, org.eclipse.jetty.io.Connection.UpgradeFrom, Sweeper.Sweepable
{ {
private static final Logger LOG = Log.getLogger(HttpConnectionOverHTTP.class); private static final Logger LOG = Log.getLogger(HttpConnectionOverHTTP.class);
@ -71,9 +72,9 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
return channel; return channel;
} }
public HttpDestinationOverHTTP getHttpDestination() public HttpDestination getHttpDestination()
{ {
return (HttpDestinationOverHTTP)delegate.getHttpDestination(); return delegate.getHttpDestination();
} }
@Override @Override
@ -116,7 +117,8 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
delegate.send(request, listener); delegate.send(request, listener);
} }
protected SendFailure send(HttpExchange exchange) @Override
public SendFailure send(HttpExchange exchange)
{ {
return delegate.send(exchange); return delegate.send(exchange);
} }
@ -238,7 +240,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
} }
@Override @Override
protected SendFailure send(HttpExchange exchange) public SendFailure send(HttpExchange exchange)
{ {
Request request = exchange.getRequest(); Request request = exchange.getRequest();
normalizeRequest(request); normalizeRequest(request);

View File

@ -221,6 +221,13 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
return 4096; return 4096;
} }
@Override
public boolean isHeaderCacheCaseSensitive()
{
// TODO get from configuration
return false;
}
@Override @Override
public boolean startResponse(HttpVersion version, int status, String reason) public boolean startResponse(HttpVersion version, int status, String reason)
{ {

View File

@ -0,0 +1,140 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client.proxy;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class ProxyProtocolClientConnectionFactory implements ClientConnectionFactory
{
private final ClientConnectionFactory connectionFactory;
private final Supplier<Origin.Address> proxiedAddressSupplier;
public ProxyProtocolClientConnectionFactory(ClientConnectionFactory connectionFactory, Supplier<Origin.Address> proxiedAddressSupplier)
{
this.connectionFactory = connectionFactory;
this.proxiedAddressSupplier = proxiedAddressSupplier;
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context)
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
Executor executor = destination.getHttpClient().getExecutor();
ProxyProtocolConnection connection = new ProxyProtocolConnection(endPoint, executor, context);
return customize(connection, context);
}
private class ProxyProtocolConnection extends AbstractConnection implements Callback
{
private final Logger LOG = Log.getLogger(ProxyProtocolConnection.class);
private final Map<String, Object> context;
public ProxyProtocolConnection(EndPoint endPoint, Executor executor, Map<String, Object> context)
{
super(endPoint, executor);
this.context = context;
}
@Override
public void onOpen()
{
super.onOpen();
writePROXYLine();
}
protected void writePROXYLine()
{
Origin.Address proxiedAddress = proxiedAddressSupplier.get();
if (proxiedAddress == null)
{
failed(new IllegalArgumentException("Missing proxied socket address"));
return;
}
String proxiedIP = proxiedAddress.getHost();
int proxiedPort = proxiedAddress.getPort();
InetSocketAddress serverSocketAddress = getEndPoint().getRemoteAddress();
InetAddress serverAddress = serverSocketAddress.getAddress();
String serverIP = serverAddress.getHostAddress();
int serverPort = serverSocketAddress.getPort();
boolean ipv6 = serverAddress instanceof Inet6Address;
String line = String.format("PROXY %s %s %s %d %d\r\n", ipv6 ? "TCP6" : "TCP4" , proxiedIP, serverIP, proxiedPort, serverPort);
if (LOG.isDebugEnabled())
LOG.debug("Writing PROXY line: {}", line.trim());
ByteBuffer buffer = ByteBuffer.wrap(line.getBytes(StandardCharsets.US_ASCII));
getEndPoint().write(this, buffer);
}
@Override
public void succeeded()
{
try
{
EndPoint endPoint = getEndPoint();
org.eclipse.jetty.io.Connection connection = connectionFactory.newConnection(endPoint, context);
if (LOG.isDebugEnabled())
LOG.debug("Written PROXY line, upgrading to {}", connection);
endPoint.upgrade(connection);
}
catch (Throwable x)
{
failed(x);
}
}
@Override
public void failed(Throwable x)
{
close();
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
@Override
public InvocationType getInvocationType()
{
return InvocationType.NON_BLOCKING;
}
@Override
public void onFillable()
{
}
}
}

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.client.util;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.AuthenticationStore;
@ -27,7 +28,6 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.B64Code;
/** /**
* Implementation of the HTTP "Basic" authentication defined in RFC 2617. * Implementation of the HTTP "Basic" authentication defined in RFC 2617.
@ -91,7 +91,8 @@ public class BasicAuthentication extends AbstractAuthentication
{ {
this.uri = uri; this.uri = uri;
this.header = header; this.header = header;
this.value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); byte[] authBytes = (user + ":" + password).getBytes(StandardCharsets.ISO_8859_1);
this.value = "Basic " + Base64.getEncoder().encodeToString(authBytes);
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import java.util.stream.Stream;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.ServerConnector;
@ -58,7 +59,7 @@ public abstract class AbstractHttpClientServerTest
serverThreads.setName("server"); serverThreads.setName("server");
server = new Server(serverThreads); server = new Server(serverThreads);
} }
connector = new ServerConnector(server, scenario.newSslContextFactory()); connector = new ServerConnector(server, scenario.newServerSslContextFactory());
connector.setPort(0); connector.setPort(0);
server.addConnector(connector); server.addConnector(connector);
server.setHandler(handler); server.setHandler(handler);
@ -67,30 +68,30 @@ public abstract class AbstractHttpClientServerTest
protected void startClient(final Scenario scenario) throws Exception protected void startClient(final Scenario scenario) throws Exception
{ {
startClient(scenario, null,null); startClient(scenario, null);
} }
protected void startClient(final Scenario scenario, HttpClientTransport transport, Consumer<HttpClient> config) throws Exception protected void startClient(final Scenario scenario, Consumer<HttpClient> config) throws Exception
{ {
if (transport==null) ClientConnector clientConnector = new ClientConnector();
transport = new HttpClientTransportOverHTTP(1); clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(scenario.newClientSslContextFactory());
HttpClientTransport transport = new HttpClientTransportOverHTTP(clientConnector);
QueuedThreadPool executor = new QueuedThreadPool(); QueuedThreadPool executor = new QueuedThreadPool();
executor.setName("client"); executor.setName("client");
clientConnector.setExecutor(executor);
Scheduler scheduler = new ScheduledExecutorScheduler("client-scheduler", false); Scheduler scheduler = new ScheduledExecutorScheduler("client-scheduler", false);
client = newHttpClient(scenario, transport); clientConnector.setScheduler(scheduler);
client.setExecutor(executor); client = newHttpClient(transport);
client.setScheduler(scheduler);
client.setSocketAddressResolver(new SocketAddressResolver.Sync()); client.setSocketAddressResolver(new SocketAddressResolver.Sync());
if (config!=null) if (config != null)
config.accept(client); config.accept(client);
client.start(); client.start();
} }
public HttpClient newHttpClient(Scenario scenario, HttpClientTransport transport) public HttpClient newHttpClient(HttpClientTransport transport)
{ {
return new HttpClient(transport, scenario.newSslContextFactory()); return new HttpClient(transport);
} }
@AfterEach @AfterEach
@ -113,9 +114,10 @@ public abstract class AbstractHttpClientServerTest
} }
} }
public static class ScenarioProvider implements ArgumentsProvider { public static class ScenarioProvider implements ArgumentsProvider
{
@Override @Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception public Stream<? extends Arguments> provideArguments(ExtensionContext context)
{ {
return Stream.of( return Stream.of(
new NormalScenario(), new NormalScenario(),
@ -125,9 +127,10 @@ public abstract class AbstractHttpClientServerTest
} }
} }
public static class NonSslScenarioProvider implements ArgumentsProvider { public static class NonSslScenarioProvider implements ArgumentsProvider
{
@Override @Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception public Stream<? extends Arguments> provideArguments(ExtensionContext context)
{ {
return Stream.of( return Stream.of(
new NormalScenario() new NormalScenario()
@ -138,12 +141,27 @@ public abstract class AbstractHttpClientServerTest
public interface Scenario public interface Scenario
{ {
default SslContextFactory newSslContextFactory() { return null; } SslContextFactory.Client newClientSslContextFactory();
SslContextFactory.Server newServerSslContextFactory();
String getScheme(); String getScheme();
} }
public static class NormalScenario implements Scenario public static class NormalScenario implements Scenario
{ {
@Override
public SslContextFactory.Client newClientSslContextFactory()
{
return null;
}
@Override
public SslContextFactory.Server newServerSslContextFactory()
{
return null;
}
@Override @Override
public String getScheme() public String getScheme()
{ {
@ -160,15 +178,27 @@ public abstract class AbstractHttpClientServerTest
public static class SslScenario implements Scenario public static class SslScenario implements Scenario
{ {
@Override @Override
public SslContextFactory newSslContextFactory() public SslContextFactory.Client newClientSslContextFactory()
{
SslContextFactory.Client result = new SslContextFactory.Client();
result.setEndpointIdentificationAlgorithm(null);
configure(result);
return result;
}
@Override
public SslContextFactory.Server newServerSslContextFactory()
{
SslContextFactory.Server result = new SslContextFactory.Server();
configure(result);
return result;
}
private void configure(SslContextFactory ssl)
{ {
Path keystorePath = MavenTestingUtils.getTestResourcePath("keystore.jks"); Path keystorePath = MavenTestingUtils.getTestResourcePath("keystore.jks");
SslContextFactory ssl = new SslContextFactory();
ssl.setEndpointIdentificationAlgorithm("");
ssl.setKeyStorePath(keystorePath.toString()); ssl.setKeyStorePath(keystorePath.toString());
ssl.setKeyStorePassword("storepwd"); ssl.setKeyStorePassword("storepwd");
return ssl;
} }
@Override @Override

View File

@ -18,11 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -36,7 +31,6 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
@ -47,6 +41,11 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ClientConnectionCloseTest extends AbstractHttpClientServerTest public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
{ {
@ParameterizedTest @ParameterizedTest
@ -87,7 +86,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
ContentResponse response = client.newRequest(host, port) ContentResponse response = client.newRequest(host, port)
@ -124,7 +123,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
CountDownLatch resultLatch = new CountDownLatch(1); CountDownLatch resultLatch = new CountDownLatch(1);
@ -185,7 +184,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8)); DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8));
@ -240,7 +239,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
ContentResponse response = client.newRequest(host, port) ContentResponse response = client.newRequest(host, port)

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -31,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -53,6 +49,9 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Disabled // Disabled by @gregw on issue #2540 - commit 621b946b10884e7308eacca241dcf8b5d6f6cff2 @Disabled // Disabled by @gregw on issue #2540 - commit 621b946b10884e7308eacca241dcf8b5d6f6cff2
public class ConnectionPoolTest public class ConnectionPoolTest
{ {
@ -83,7 +82,7 @@ public class ConnectionPoolTest
transport.setConnectionPoolFactory(factory); transport.setConnectionPoolFactory(factory);
server.start(); server.start();
client = new HttpClient(transport, null); client = new HttpClient(transport);
client.start(); client.start();
} }
@ -115,7 +114,7 @@ public class ConnectionPoolTest
start(factory, new EmptyServerHandler() start(factory, new EmptyServerHandler()
{ {
@Override @Override
protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
switch (HttpMethod.fromString(request.getMethod())) switch (HttpMethod.fromString(request.getMethod()))
{ {

View File

@ -18,10 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -30,12 +26,16 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@Disabled @Disabled
public class ExternalSiteTest public class ExternalSiteTest
{ {
@ -44,7 +44,7 @@ public class ExternalSiteTest
@BeforeEach @BeforeEach
public void prepare() throws Exception public void prepare() throws Exception
{ {
client = new HttpClient(new SslContextFactory()); client = new HttpClient();
client.start(); client.start();
} }
@ -54,6 +54,7 @@ public class ExternalSiteTest
client.stop(); client.stop();
} }
@Tag("external")
@Test @Test
public void testExternalSite() throws Exception public void testExternalSite() throws Exception
{ {
@ -64,39 +65,29 @@ public class ExternalSiteTest
assumeCanConnectTo(host, port); assumeCanConnectTo(host, port);
final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch1 = new CountDownLatch(1);
client.newRequest(host, port).send(new Response.CompleteListener() client.newRequest(host, port).send(result ->
{ {
@Override assertTrue(result.isSucceeded());
public void onComplete(Result result) assertEquals(200, result.getResponse().getStatus());
{ latch1.countDown();
if (!result.isFailed() && result.getResponse().getStatus() == 200)
latch1.countDown();
}
}); });
assertTrue(latch1.await(10, TimeUnit.SECONDS)); assertTrue(latch1.await(15, TimeUnit.SECONDS));
// Try again the same URI, but without specifying the port // Try again the same URI, but without specifying the port
final CountDownLatch latch2 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1);
client.newRequest("http://" + host).send(new Response.CompleteListener() client.newRequest("http://" + host).send(result ->
{ {
@Override assertTrue(result.isSucceeded());
public void onComplete(Result result) assertEquals(200, result.getResponse().getStatus());
{ latch2.countDown();
assertTrue(result.isSucceeded());
assertEquals(200, result.getResponse().getStatus());
latch2.countDown();
}
}); });
assertTrue(latch2.await(10, TimeUnit.SECONDS)); assertTrue(latch2.await(15, TimeUnit.SECONDS));
} }
@Tag("external")
@Test @Test
public void testExternalSSLSite() throws Exception public void testExternalSSLSite() throws Exception
{ {
client.stop();
client = new HttpClient(new SslContextFactory());
client.start();
String host = "api-3t.paypal.com"; String host = "api-3t.paypal.com";
int port = 443; int port = 443;
@ -104,18 +95,16 @@ public class ExternalSiteTest
assumeCanConnectTo(host, port); assumeCanConnectTo(host, port);
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
client.newRequest(host, port).scheme("https").path("/nvp").send(new Response.CompleteListener() client.newRequest(host, port).scheme("https").path("/nvp").send(result ->
{ {
@Override assertTrue(result.isSucceeded());
public void onComplete(Result result) assertEquals(200, result.getResponse().getStatus());
{ latch.countDown();
if (result.isSucceeded() && result.getResponse().getStatus() == 200)
latch.countDown();
}
}); });
assertTrue(latch.await(5, TimeUnit.SECONDS)); assertTrue(latch.await(15, TimeUnit.SECONDS));
} }
@Tag("external")
@Test @Test
public void testExternalSiteWrongProtocol() throws Exception public void testExternalSiteWrongProtocol() throws Exception
{ {
@ -129,14 +118,7 @@ public class ExternalSiteTest
{ {
final CountDownLatch latch = new CountDownLatch(3); final CountDownLatch latch = new CountDownLatch(3);
client.newRequest(host, port) client.newRequest(host, port)
.onResponseFailure(new Response.FailureListener() .onResponseFailure((response, failure) -> latch.countDown())
{
@Override
public void onFailure(Response response, Throwable failure)
{
latch.countDown();
}
})
.send(new Response.Listener.Adapter() .send(new Response.Listener.Adapter()
{ {
@Override @Override
@ -152,10 +134,11 @@ public class ExternalSiteTest
latch.countDown(); latch.countDown();
} }
}); });
assertTrue(latch.await(10, TimeUnit.SECONDS)); assertTrue(latch.await(15, TimeUnit.SECONDS));
} }
} }
@Tag("external")
@Test @Test
public void testExternalSiteRedirect() throws Exception public void testExternalSiteRedirect() throws Exception
{ {
@ -168,6 +151,7 @@ public class ExternalSiteTest
ContentResponse response = client.newRequest(host, port) ContentResponse response = client.newRequest(host, port)
.scheme(HttpScheme.HTTPS.asString()) .scheme(HttpScheme.HTTPS.asString())
.path("/twitter") .path("/twitter")
.timeout(15, TimeUnit.SECONDS)
.send(); .send();
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
} }
@ -180,7 +164,7 @@ public class ExternalSiteTest
} }
catch (Throwable x) catch (Throwable x)
{ {
assumeTrue(x == null, "Unable to connect"); assumeTrue(false, "Unable to connect");
} }
} }
} }

View File

@ -18,19 +18,16 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException; import java.io.IOException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -40,11 +37,14 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
/** /**
* This test class runs tests to make sure that hostname verification (http://www.ietf.org/rfc/rfc2818.txt * This test class runs tests to make sure that hostname verification (http://www.ietf.org/rfc/rfc2818.txt
* section 3.1) is configurable in SslContextFactory and works as expected. * section 3.1) is configurable in SslContextFactory and works as expected.
@ -52,7 +52,7 @@ import org.junit.jupiter.api.Test;
@Disabled @Disabled
public class HostnameVerificationTest public class HostnameVerificationTest
{ {
private SslContextFactory clientSslContextFactory = new SslContextFactory(); private SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client();
private Server server; private Server server;
private HttpClient client; private HttpClient client;
private NetworkConnector connector; private NetworkConnector connector;
@ -64,7 +64,7 @@ public class HostnameVerificationTest
serverThreads.setName("server"); serverThreads.setName("server");
server = new Server(serverThreads); server = new Server(serverThreads);
SslContextFactory serverSslContextFactory = new SslContextFactory(); SslContextFactory.Server serverSslContextFactory = new SslContextFactory.Server();
serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
serverSslContextFactory.setKeyStorePassword("storepwd"); serverSslContextFactory.setKeyStorePassword("storepwd");
connector = new ServerConnector(server, serverSslContextFactory); connector = new ServerConnector(server, serverSslContextFactory);
@ -72,7 +72,7 @@ public class HostnameVerificationTest
server.setHandler(new DefaultHandler() server.setHandler(new DefaultHandler()
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
response.getWriter().write("foobar"); response.getWriter().write("foobar");
@ -80,14 +80,19 @@ public class HostnameVerificationTest
}); });
server.start(); server.start();
// keystore contains a hostname which doesn't match localhost // The keystore contains a hostname which doesn't match localhost
clientSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); clientSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
clientSslContextFactory.setKeyStorePassword("storepwd"); clientSslContextFactory.setKeyStorePassword("storepwd");
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(clientSslContextFactory);
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(clientSslContextFactory); clientConnector.setExecutor(clientThreads);
client.setExecutor(clientThreads);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start(); client.start();
} }
@ -112,9 +117,7 @@ public class HostnameVerificationTest
clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS"); clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
String uri = "https://localhost:" + connector.getLocalPort() + "/"; String uri = "https://localhost:" + connector.getLocalPort() + "/";
ExecutionException x = assertThrows(ExecutionException.class, ()->{ ExecutionException x = assertThrows(ExecutionException.class, ()-> client.GET(uri));
client.GET(uri);
});
Throwable cause = x.getCause(); Throwable cause = x.getCause();
assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class)); assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class));
Throwable root = cause.getCause().getCause(); Throwable root = cause.getCause().getCause();

View File

@ -44,9 +44,11 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Response.Listener; import org.eclipse.jetty.client.api.Response.Listener;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.AbstractAuthentication;
import org.eclipse.jetty.client.util.BasicAuthentication; import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.DigestAuthentication; import org.eclipse.jetty.client.util.DigestAuthentication;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintMapping;
@ -57,7 +59,6 @@ import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.security.authentication.DigestAuthenticator; import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
@ -67,6 +68,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.eclipse.jetty.client.api.Authentication.ANY_REALM;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -137,7 +139,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
{ {
startBasic(scenario, new EmptyServerHandler()); startBasic(scenario, new EmptyServerHandler());
URI uri = URI.create(scenario.getScheme() + "://localhost:" + connector.getLocalPort()); URI uri = URI.create(scenario.getScheme() + "://localhost:" + connector.getLocalPort());
test_Authentication(scenario, new BasicAuthentication(uri, Authentication.ANY_REALM, "basic", "basic")); test_Authentication(scenario, new BasicAuthentication(uri, ANY_REALM, "basic", "basic"));
} }
@ParameterizedTest @ParameterizedTest
@ -155,7 +157,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
{ {
startDigest(scenario, new EmptyServerHandler()); startDigest(scenario, new EmptyServerHandler());
URI uri = URI.create(scenario.getScheme() + "://localhost:" + connector.getLocalPort()); URI uri = URI.create(scenario.getScheme() + "://localhost:" + connector.getLocalPort());
test_Authentication(scenario, new DigestAuthentication(uri, Authentication.ANY_REALM, "digest", "digest")); test_Authentication(scenario, new DigestAuthentication(uri, ANY_REALM, "digest", "digest"));
} }
private void test_Authentication(final Scenario scenario, Authentication authentication) throws Exception private void test_Authentication(final Scenario scenario, Authentication authentication) throws Exception
@ -227,16 +229,19 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void test_BasicAuthentication_ThenRedirect(Scenario scenario) throws Exception public void test_BasicAuthentication_ThenRedirect(Scenario scenario) throws Exception
{ {
startBasic(scenario, new AbstractHandler() startBasic(scenario, new EmptyServerHandler()
{ {
private final AtomicInteger requests = new AtomicInteger(); private final AtomicInteger requests = new AtomicInteger();
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); int r = requests.incrementAndGet();
if (requests.incrementAndGet() == 1) if (r == 1)
response.sendRedirect(URIUtil.newURI(scenario.getScheme(), request.getServerName(), request.getServerPort(), request.getRequestURI(), null)); {
String path = request.getRequestURI() + "/" + r;
response.sendRedirect(URIUtil.newURI(scenario.getScheme(), request.getServerName(), request.getServerPort(), path, null));
}
} }
}); });
@ -269,12 +274,11 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void test_Redirect_ThenBasicAuthentication(Scenario scenario) throws Exception public void test_Redirect_ThenBasicAuthentication(Scenario scenario) throws Exception
{ {
startBasic(scenario, new AbstractHandler() startBasic(scenario, new EmptyServerHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true);
if (request.getRequestURI().endsWith("/redirect")) if (request.getRequestURI().endsWith("/redirect"))
response.sendRedirect(URIUtil.newURI(scenario.getScheme(), request.getServerName(), request.getServerPort(), "/secure", null)); response.sendRedirect(URIUtil.newURI(scenario.getScheme(), request.getServerName(), request.getServerPort(), "/secure", null));
} }
@ -571,61 +575,57 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
assertTrue(resultLatch.await(5, TimeUnit.SECONDS)); assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
} }
private static class GeneratingContentProvider implements ContentProvider @ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void test_InfiniteAuthentication(Scenario scenario) throws Exception
{ {
private static final ByteBuffer DONE = ByteBuffer.allocate(0); String authType = "Authenticate";
start(scenario, new EmptyServerHandler()
private final IntFunction<ByteBuffer> generator;
private GeneratingContentProvider(IntFunction<ByteBuffer> generator)
{ {
this.generator = generator; @Override
} protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
@Override
public long getLength()
{
return -1;
}
@Override
public boolean isReproducible()
{
return true;
}
@Override
public Iterator<ByteBuffer> iterator()
{
return new Iterator<ByteBuffer>()
{ {
private int index; // Always reply with a 401 to see if the client
public ByteBuffer current; // can handle an infinite authentication loop.
response.setStatus(HttpStatus.UNAUTHORIZED_401);
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), authType);
}
});
@Override AuthenticationStore authenticationStore = client.getAuthenticationStore();
@SuppressWarnings("ReferenceEquality") URI uri = URI.create(scenario.getScheme() + "://localhost:" + connector.getLocalPort());
public boolean hasNext() authenticationStore.addAuthentication(new AbstractAuthentication(uri, Authentication.ANY_REALM)
{
@Override
public String getType()
{
return authType;
}
@Override
public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context)
{
return new Result()
{ {
if (current == null) @Override
public URI getURI()
{ {
current = generator.apply(index++); return uri;
if (current == null)
current = DONE;
} }
return current != DONE;
}
@Override @Override
public ByteBuffer next() public void apply(Request request)
{ {
ByteBuffer result = current; }
current = null; };
if (result == null) }
throw new NoSuchElementException(); });
return result;
} ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
}; .scheme(scenario.getScheme())
} .send();
assertEquals(HttpStatus.UNAUTHORIZED_401, response.getStatus());
} }
@Test @Test
@ -801,4 +801,61 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
assertEquals("thermostat", headerInfo.getParameter("realm")); assertEquals("thermostat", headerInfo.getParameter("realm"));
assertEquals(headerInfo.getParameter("nonce"), "1523430383="); assertEquals(headerInfo.getParameter("nonce"), "1523430383=");
} }
private static class GeneratingContentProvider implements ContentProvider
{
private static final ByteBuffer DONE = ByteBuffer.allocate(0);
private final IntFunction<ByteBuffer> generator;
private GeneratingContentProvider(IntFunction<ByteBuffer> generator)
{
this.generator = generator;
}
@Override
public long getLength()
{
return -1;
}
@Override
public boolean isReproducible()
{
return true;
}
@Override
public Iterator<ByteBuffer> iterator()
{
return new Iterator<ByteBuffer>()
{
private int index;
public ByteBuffer current;
@Override
@SuppressWarnings("ReferenceEquality")
public boolean hasNext()
{
if (current == null)
{
current = generator.apply(index++);
if (current == null)
current = DONE;
}
return current != DONE;
}
@Override
public ByteBuffer next()
{
ByteBuffer result = current;
current = null;
if (result == null)
throw new NoSuchElementException();
return result;
}
};
}
}
} }

View File

@ -18,11 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -31,13 +26,17 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.FuturePromise;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTest public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTest
{ {
@ParameterizedTest @ParameterizedTest
@ -59,7 +58,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
assertNotNull(response); assertNotNull(response);
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination; HttpDestination httpDestination = (HttpDestination)destination;
DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool();
assertTrue(connectionPool.getActiveConnections().isEmpty()); assertTrue(connectionPool.getActiveConnections().isEmpty());
assertTrue(connectionPool.getIdleConnections().isEmpty()); assertTrue(connectionPool.getIdleConnections().isEmpty());
@ -94,7 +93,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection; HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection;
assertFalse(httpConnection.getEndPoint().isOpen()); assertFalse(httpConnection.getEndPoint().isOpen());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination; HttpDestination httpDestination = (HttpDestination)destination;
DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool();
assertTrue(connectionPool.getActiveConnections().isEmpty()); assertTrue(connectionPool.getActiveConnections().isEmpty());
assertTrue(connectionPool.getIdleConnections().isEmpty()); assertTrue(connectionPool.getIdleConnections().isEmpty());

View File

@ -18,10 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -40,9 +36,12 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientFailureTest public class HttpClientFailureTest
{ {
private Server server; private Server server;
@ -75,7 +74,7 @@ public class HttpClientFailureTest
startServer(new EmptyServerHandler()); startServer(new EmptyServerHandler());
final AtomicReference<HttpConnectionOverHTTP> connectionRef = new AtomicReference<>(); final AtomicReference<HttpConnectionOverHTTP> connectionRef = new AtomicReference<>();
client = new HttpClient(new HttpClientTransportOverHTTP() client = new HttpClient(new HttpClientTransportOverHTTP(1)
{ {
@Override @Override
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise) protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
@ -84,15 +83,14 @@ public class HttpClientFailureTest
connectionRef.set(connection); connectionRef.set(connection);
return connection; return connection;
} }
}, null); });
client.start(); client.start();
assertThrows(ExecutionException.class, ()->{ assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort()) client.newRequest("localhost", connector.getLocalPort())
.onRequestHeaders(request -> connectionRef.get().getEndPoint().close()) .onRequestHeaders(request -> connectionRef.get().getEndPoint().close())
.timeout(5, TimeUnit.SECONDS) .timeout(5, TimeUnit.SECONDS)
.send(); .send());
});
DuplexConnectionPool connectionPool = (DuplexConnectionPool)connectionRef.get().getHttpDestination().getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)connectionRef.get().getHttpDestination().getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
@ -106,7 +104,7 @@ public class HttpClientFailureTest
startServer(new EmptyServerHandler()); startServer(new EmptyServerHandler());
final AtomicReference<HttpConnectionOverHTTP> connectionRef = new AtomicReference<>(); final AtomicReference<HttpConnectionOverHTTP> connectionRef = new AtomicReference<>();
client = new HttpClient(new HttpClientTransportOverHTTP() client = new HttpClient(new HttpClientTransportOverHTTP(1)
{ {
@Override @Override
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise) protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
@ -115,7 +113,7 @@ public class HttpClientFailureTest
connectionRef.set(connection); connectionRef.set(connection);
return connection; return connection;
} }
}, null); });
client.start(); client.start();
final CountDownLatch commitLatch = new CountDownLatch(1); final CountDownLatch commitLatch = new CountDownLatch(1);
@ -155,98 +153,4 @@ public class HttpClientFailureTest
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
assertEquals(0, connectionPool.getIdleConnections().size()); assertEquals(0, connectionPool.getIdleConnections().size());
} }
/*
@Test
public void test_ExchangeIsComplete_WhenRequestFailsMidway_WithResponse() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// Echo back
IO.copy(request.getInputStream(), response.getOutputStream());
}
});
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
// The second ByteBuffer set to null will throw an exception
.content(new ContentProvider()
{
@Override
public long getLength()
{
return -1;
}
@Override
public Iterator<ByteBuffer> iterator()
{
return new Iterator<ByteBuffer>()
{
@Override
public boolean hasNext()
{
return true;
}
@Override
public ByteBuffer next()
{
throw new NoSuchElementException("explicitly_thrown_by_test");
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
})
.send(new Response.Listener.Adapter()
{
@Override
public void onComplete(Result result)
{
latch.countDown();
}
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Test
public void test_ExchangeIsComplete_WhenRequestFails_WithNoResponse() throws Exception
{
start(new EmptyServerHandler());
final CountDownLatch latch = new CountDownLatch(1);
final String host = "localhost";
final int port = connector.getLocalPort();
client.newRequest(host, port)
.scheme(scheme)
.onRequestBegin(new Request.BeginListener()
{
@Override
public void onBegin(Request request)
{
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
destination.getConnectionPool().getActiveConnections().peek().close();
}
})
.send(new Response.Listener.Adapter()
{
@Override
public void onComplete(Result result)
{
latch.countDown();
}
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
*/
} }

View File

@ -21,9 +21,9 @@ package org.eclipse.jetty.client;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -35,7 +35,6 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.B64Code;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
@ -82,7 +81,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
{ {
final String user = "foo"; final String user = "foo";
final String password = "bar"; final String password = "bar";
final String credentials = B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); final String credentials = Base64.getEncoder().encodeToString((user + ":" + password).getBytes(StandardCharsets.ISO_8859_1));
final String serverHost = "server"; final String serverHost = "server";
final String realm = "test_realm"; final String realm = "test_realm";
final int status = HttpStatus.NO_CONTENT_204; final int status = HttpStatus.NO_CONTENT_204;
@ -162,7 +161,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
{ {
String user = "foo"; String user = "foo";
String password = "bar"; String password = "bar";
String credentials = B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); String credentials = Base64.getEncoder().encodeToString((user + ":" + password).getBytes(StandardCharsets.ISO_8859_1));
String proxyHost = "localhost"; String proxyHost = "localhost";
String serverHost = "server"; String serverHost = "server";
int serverPort = HttpScheme.HTTP.is(scenario.getScheme()) ? 80 : 443; int serverPort = HttpScheme.HTTP.is(scenario.getScheme()) ? 80 : 443;

View File

@ -18,14 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -34,7 +26,9 @@ import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -48,13 +42,20 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.IO;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientRedirectTest extends AbstractHttpClientServerTest public class HttpClientRedirectTest extends AbstractHttpClientServerTest
{ {
@ParameterizedTest @ParameterizedTest
@ -128,14 +129,13 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
{ {
start(scenario, new RedirectHandler()); start(scenario, new RedirectHandler());
ExecutionException x = assertThrows(ExecutionException.class, ()->{ ExecutionException x = assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort()) client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme()) .scheme(scenario.getScheme())
.method(HttpMethod.DELETE) .method(HttpMethod.DELETE)
.path("/301/localhost/done") .path("/301/localhost/done")
.timeout(5, TimeUnit.SECONDS) .timeout(5, TimeUnit.SECONDS)
.send(); .send());
});
HttpResponseException xx = (HttpResponseException)x.getCause(); HttpResponseException xx = (HttpResponseException)x.getCause();
Response response = xx.getResponse(); Response response = xx.getResponse();
assertNotNull(response); assertNotNull(response);
@ -170,13 +170,12 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
start(scenario, new RedirectHandler()); start(scenario, new RedirectHandler());
client.setMaxRedirects(1); client.setMaxRedirects(1);
ExecutionException x = assertThrows(ExecutionException.class, ()->{ ExecutionException x = assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort()) client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme()) .scheme(scenario.getScheme())
.path("/303/localhost/302/localhost/done") .path("/303/localhost/302/localhost/done")
.timeout(5, TimeUnit.SECONDS) .timeout(5, TimeUnit.SECONDS)
.send(); .send());
});
HttpResponseException xx = (HttpResponseException)x.getCause(); HttpResponseException xx = (HttpResponseException)x.getCause();
Response response = xx.getResponse(); Response response = xx.getResponse();
assertNotNull(response); assertNotNull(response);
@ -269,12 +268,11 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void testRedirectWithWrongScheme(Scenario scenario) throws Exception public void testRedirectWithWrongScheme(Scenario scenario) throws Exception
{ {
start(scenario, new AbstractHandler() start(scenario, new EmptyServerHandler()
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true);
response.setStatus(303); response.setStatus(303);
response.setHeader("Location", "ssh://localhost:" + connector.getLocalPort() + "/path"); response.setHeader("Location", "ssh://localhost:" + connector.getLocalPort() + "/path");
} }
@ -439,12 +437,11 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
public void testRedirectWithCorruptedBody(Scenario scenario) throws Exception public void testRedirectWithCorruptedBody(Scenario scenario) throws Exception
{ {
byte[] bytes = "ok".getBytes(StandardCharsets.UTF_8); byte[] bytes = "ok".getBytes(StandardCharsets.UTF_8);
start(scenario, new AbstractHandler() start(scenario, new EmptyServerHandler()
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true);
if (target.startsWith("/redirect")) if (target.startsWith("/redirect"))
{ {
response.setStatus(HttpStatus.SEE_OTHER_303); response.setStatus(HttpStatus.SEE_OTHER_303);
@ -471,6 +468,60 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
assertArrayEquals(bytes, response.getContent()); assertArrayEquals(bytes, response.getContent());
} }
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testRedirectToSameURL(Scenario scenario) throws Exception
{
start(scenario, new EmptyServerHandler()
{
@Override
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
{
response.setStatus(HttpStatus.SEE_OTHER_303);
response.setHeader(HttpHeader.LOCATION.asString(), request.getRequestURI());
}
});
ExecutionException x = assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.send());
assertThat(x.getCause(), Matchers.instanceOf(HttpResponseException.class));
}
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testInfiniteRedirectLoopMustTimeout(Scenario scenario) throws Exception
{
AtomicLong counter = new AtomicLong();
start(scenario, new EmptyServerHandler()
{
@Override
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
{
try
{
Thread.sleep(200);
response.setStatus(HttpStatus.SEE_OTHER_303);
response.setHeader(HttpHeader.LOCATION.asString(), "/" + counter.getAndIncrement());
}
catch (InterruptedException x)
{
throw new RuntimeException(x);
}
}
});
assertThrows(TimeoutException.class, () ->
{
client.setMaxRedirects(-1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.timeout(1, TimeUnit.SECONDS)
.send();
});
}
private void testSameMethodRedirect(final Scenario scenario, final HttpMethod method, int redirectCode) throws Exception private void testSameMethodRedirect(final Scenario scenario, final HttpMethod method, int redirectCode) throws Exception
{ {
testMethodRedirect(scenario, method, method, redirectCode); testMethodRedirect(scenario, method, method, redirectCode);
@ -519,10 +570,10 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
} }
private class RedirectHandler extends AbstractHandler private class RedirectHandler extends EmptyServerHandler
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{ {
try try
{ {
@ -551,10 +602,6 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
// Echo content back // Echo content back
IO.copy(request.getInputStream(), response.getOutputStream()); IO.copy(request.getInputStream(), response.getOutputStream());
} }
finally
{
baseRequest.setHandled(true);
}
} }
} }
} }

View File

@ -35,15 +35,18 @@ import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ExecutorThreadPool; import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
@ -66,7 +69,7 @@ public class HttpClientTLSTest
private ServerConnector connector; private ServerConnector connector;
private HttpClient client; private HttpClient client;
private void startServer(SslContextFactory sslContextFactory, Handler handler) throws Exception private void startServer(SslContextFactory.Server sslContextFactory, Handler handler) throws Exception
{ {
ExecutorThreadPool serverThreads = new ExecutorThreadPool(); ExecutorThreadPool serverThreads = new ExecutorThreadPool();
serverThreads.setName("server"); serverThreads.setName("server");
@ -79,22 +82,37 @@ public class HttpClientTLSTest
server.start(); server.start();
} }
private void startClient(SslContextFactory sslContextFactory) throws Exception private void startClient(SslContextFactory.Client sslContextFactory) throws Exception
{ {
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(sslContextFactory);
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(sslContextFactory); clientConnector.setExecutor(clientThreads);
client.setExecutor(clientThreads); client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start(); client.start();
} }
private SslContextFactory createSslContextFactory() private SslContextFactory.Server createServerSslContextFactory()
{
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
configureSslContextFactory(sslContextFactory);
return sslContextFactory;
}
private SslContextFactory.Client createClientSslContextFactory()
{
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
configureSslContextFactory(sslContextFactory);
sslContextFactory.setEndpointIdentificationAlgorithm(null);
return sslContextFactory;
}
private void configureSslContextFactory(SslContextFactory sslContextFactory)
{ {
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setEndpointIdentificationAlgorithm("");
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyStorePassword("storepwd");
return sslContextFactory;
} }
@AfterEach @AfterEach
@ -109,7 +127,7 @@ public class HttpClientTLSTest
@Test @Test
public void testNoCommonTLSProtocol() throws Exception public void testNoCommonTLSProtocol() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
serverTLSFactory.setIncludeProtocols("TLSv1.3"); serverTLSFactory.setIncludeProtocols("TLSv1.3");
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
@ -123,7 +141,7 @@ public class HttpClientTLSTest
} }
}); });
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
clientTLSFactory.setIncludeProtocols("TLSv1.2"); clientTLSFactory.setIncludeProtocols("TLSv1.2");
startClient(clientTLSFactory); startClient(clientTLSFactory);
@ -150,7 +168,7 @@ public class HttpClientTLSTest
@Test @Test
public void testNoCommonTLSCiphers() throws Exception public void testNoCommonTLSCiphers() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
serverTLSFactory.setIncludeCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA"); serverTLSFactory.setIncludeCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
@ -164,7 +182,7 @@ public class HttpClientTLSTest
} }
}); });
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
clientTLSFactory.setExcludeCipherSuites(".*_SHA$"); clientTLSFactory.setExcludeCipherSuites(".*_SHA$");
startClient(clientTLSFactory); startClient(clientTLSFactory);
@ -191,7 +209,7 @@ public class HttpClientTLSTest
@Test @Test
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnServer() throws Exception public void testMismatchBetweenTLSProtocolAndTLSCiphersOnServer() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
// TLS 1.1 protocol, but only TLS 1.2 ciphers. // TLS 1.1 protocol, but only TLS 1.2 ciphers.
serverTLSFactory.setIncludeProtocols("TLSv1.1"); serverTLSFactory.setIncludeProtocols("TLSv1.1");
serverTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); serverTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
@ -207,7 +225,7 @@ public class HttpClientTLSTest
} }
}); });
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
startClient(clientTLSFactory); startClient(clientTLSFactory);
CountDownLatch clientLatch = new CountDownLatch(1); CountDownLatch clientLatch = new CountDownLatch(1);
@ -236,7 +254,7 @@ public class HttpClientTLSTest
@Test @Test
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
CountDownLatch serverLatch = new CountDownLatch(1); CountDownLatch serverLatch = new CountDownLatch(1);
@ -249,7 +267,7 @@ public class HttpClientTLSTest
} }
}); });
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
// TLS 1.1 protocol, but only TLS 1.2 ciphers. // TLS 1.1 protocol, but only TLS 1.2 ciphers.
clientTLSFactory.setIncludeProtocols("TLSv1.1"); clientTLSFactory.setIncludeProtocols("TLSv1.1");
clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
@ -278,7 +296,7 @@ public class HttpClientTLSTest
@Test @Test
public void testHandshakeSucceeded() throws Exception public void testHandshakeSucceeded() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
CountDownLatch serverLatch = new CountDownLatch(1); CountDownLatch serverLatch = new CountDownLatch(1);
@ -291,7 +309,7 @@ public class HttpClientTLSTest
} }
}); });
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
startClient(clientTLSFactory); startClient(clientTLSFactory);
CountDownLatch clientLatch = new CountDownLatch(1); CountDownLatch clientLatch = new CountDownLatch(1);
@ -317,7 +335,7 @@ public class HttpClientTLSTest
@Test @Test
public void testHandshakeSucceededWithSessionResumption() throws Exception public void testHandshakeSucceededWithSessionResumption() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
AtomicReference<byte[]> serverSession = new AtomicReference<>(); AtomicReference<byte[]> serverSession = new AtomicReference<>();
@ -330,7 +348,7 @@ public class HttpClientTLSTest
} }
}); });
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
startClient(clientTLSFactory); startClient(clientTLSFactory);
AtomicReference<byte[]> clientSession = new AtomicReference<>(); AtomicReference<byte[]> clientSession = new AtomicReference<>();
@ -397,10 +415,10 @@ public class HttpClientTLSTest
@Test @Test
public void testClientRawCloseDoesNotInvalidateSession() throws Exception public void testClientRawCloseDoesNotInvalidateSession() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory clientTLSFactory = createClientSslContextFactory();
clientTLSFactory.start(); clientTLSFactory.start();
String host = "localhost"; String host = "localhost";
@ -452,13 +470,17 @@ public class HttpClientTLSTest
@Test @Test
public void testServerRawCloseDetectedByClient() throws Exception public void testServerRawCloseDetectedByClient() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory serverTLSFactory = createServerSslContextFactory();
serverTLSFactory.start(); serverTLSFactory.start();
try (ServerSocket server = new ServerSocket(0)) try (ServerSocket server = new ServerSocket(0))
{ {
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(createSslContextFactory()) clientConnector.setExecutor(clientThreads);
clientConnector.setSslContextFactory(createClientSslContextFactory());
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector))
{ {
@Override @Override
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory) protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
@ -468,7 +490,6 @@ public class HttpClientTLSTest
return ssl; return ssl;
} }
}; };
client.setExecutor(clientThreads);
client.start(); client.start();
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
@ -488,7 +509,7 @@ public class HttpClientTLSTest
while (true) while (true)
{ {
String line = reader.readLine(); String line = reader.readLine();
if (line == null || line.isEmpty()) if (StringUtil.isEmpty(line))
break; break;
} }
@ -522,10 +543,10 @@ public class HttpClientTLSTest
@Test @Test
public void testHostNameVerificationFailure() throws Exception public void testHostNameVerificationFailure() throws Exception
{ {
SslContextFactory serverTLSFactory = createSslContextFactory(); SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler()); startServer(serverTLSFactory, new EmptyServerHandler());
SslContextFactory clientTLSFactory = createSslContextFactory(); SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
// Make sure the host name is not verified at the TLS level. // Make sure the host name is not verified at the TLS level.
clientTLSFactory.setEndpointIdentificationAlgorithm(null); clientTLSFactory.setEndpointIdentificationAlgorithm(null);
// Add host name verification after the TLS handshake. // Add host name verification after the TLS handshake.

View File

@ -50,7 +50,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -65,7 +64,6 @@ import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider;
@ -79,8 +77,10 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.Net;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
@ -111,7 +111,6 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{ {
public WorkDir testdir; public WorkDir testdir;
@ParameterizedTest @ParameterizedTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void testStoppingClosesConnections(Scenario scenario) throws Exception public void testStoppingClosesConnections(Scenario scenario) throws Exception
@ -124,7 +123,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
Response response = client.GET(scenario.getScheme() + "://" + host + ":" + port + path); Response response = client.GET(scenario.getScheme() + "://" + host + ":" + port + path);
assertEquals(200, response.getStatus()); assertEquals(200, response.getStatus());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
long start = System.nanoTime(); long start = System.nanoTime();
@ -187,7 +186,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
response.getOutputStream().write(data); response.getOutputStream().write(data);
baseRequest.setHandled(true); baseRequest.setHandled(true);
@ -212,7 +211,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
ServletOutputStream output = response.getOutputStream(); ServletOutputStream output = response.getOutputStream();
@ -245,7 +244,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
ServletOutputStream output = response.getOutputStream(); ServletOutputStream output = response.getOutputStream();
@ -282,7 +281,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
String value = request.getParameter(paramName); String value = request.getParameter(paramName);
@ -315,7 +314,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
String value = request.getParameter(paramName); String value = request.getParameter(paramName);
@ -349,7 +348,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
consume(request.getInputStream(), true); consume(request.getInputStream(), true);
@ -381,7 +380,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
consume(request.getInputStream(), true); consume(request.getInputStream(), true);
@ -412,7 +411,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
consume(request.getInputStream(), true); consume(request.getInputStream(), true);
@ -493,7 +492,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
if (target.endsWith("/one")) if (target.endsWith("/one"))
baseRequest.getHttpChannel().getEndPoint().close(); baseRequest.getHttpChannel().getEndPoint().close();
@ -612,7 +611,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
// Echo back // Echo back
IO.copy(request.getInputStream(), response.getOutputStream()); IO.copy(request.getInputStream(), response.getOutputStream());
@ -634,7 +633,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@Override @Override
public Iterator<ByteBuffer> iterator() public Iterator<ByteBuffer> iterator()
{ {
return new Iterator<ByteBuffer>() return new Iterator<>()
{ {
@Override @Override
public boolean hasNext() public boolean hasNext()
@ -681,7 +680,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
.scheme(scenario.getScheme()) .scheme(scenario.getScheme())
.onRequestBegin(request -> .onRequestBegin(request ->
{ {
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
connectionPool.getActiveConnections().iterator().next().close(); connectionPool.getActiveConnections().iterator().next().close();
}) })
@ -706,7 +705,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
{ {
try try
{ {
@ -722,13 +721,12 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final String host = "localhost"; final String host = "localhost";
final int port = connector.getLocalPort(); final int port = connector.getLocalPort();
assertThrows(TimeoutException.class, ()->{ assertThrows(TimeoutException.class, () ->
client.newRequest(host, port) client.newRequest(host, port)
.scheme(scenario.getScheme()) .scheme(scenario.getScheme())
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS) .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS) .timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
.send(); .send());
});
// Make another request without specifying the idle timeout, should not fail // Make another request without specifying the idle timeout, should not fail
ContentResponse response = client.newRequest(host, port) ContentResponse response = client.newRequest(host, port)
@ -744,6 +742,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void testSendToIPv6Address(Scenario scenario) throws Exception public void testSendToIPv6Address(Scenario scenario) throws Exception
{ {
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
start(scenario, new EmptyServerHandler()); start(scenario, new EmptyServerHandler());
ContentResponse response = client.newRequest("[::1]", connector.getLocalPort()) ContentResponse response = client.newRequest("[::1]", connector.getLocalPort())
@ -763,7 +762,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
response.setHeader(headerName, "X"); response.setHeader(headerName, "X");
@ -821,7 +820,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
response.getOutputStream().write(new byte[length]); response.getOutputStream().write(new byte[length]);
@ -882,14 +881,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest
public void testConnectHostWithMultipleAddresses(Scenario scenario) throws Exception public void testConnectHostWithMultipleAddresses(Scenario scenario) throws Exception
{ {
startServer(scenario, new EmptyServerHandler()); startServer(scenario, new EmptyServerHandler());
startClient(scenario, null, client -> startClient(scenario, client ->
{ {
client.setSocketAddressResolver(new SocketAddressResolver.Async(client.getExecutor(), client.getScheduler(), 5000) client.setSocketAddressResolver(new SocketAddressResolver.Async(client.getExecutor(), client.getScheduler(), 5000)
{ {
@Override @Override
public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise) public void resolve(String host, int port, Promise<List<InetSocketAddress>> promise)
{ {
super.resolve(host, port, new Promise<List<InetSocketAddress>>() super.resolve(host, port, new Promise<>()
{ {
@Override @Override
public void succeeded(List<InetSocketAddress> result) public void succeeded(List<InetSocketAddress> result)
@ -925,7 +924,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent")); ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent"));
@ -959,7 +958,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent")); ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent"));
@ -1161,7 +1160,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
response.getOutputStream().write(content); response.getOutputStream().write(content);
@ -1205,7 +1204,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
assertEquals(host, request.getServerName()); assertEquals(host, request.getServerName());
@ -1227,7 +1226,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
// Send the headers at this point, then write the content // Send the headers at this point, then write the content
byte[] content = "TEST".getBytes("UTF-8"); byte[] content = "TEST".getBytes("UTF-8");
@ -1255,7 +1254,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
// Send the headers at this point, then write the content // Send the headers at this point, then write the content
response.flushBuffer(); response.flushBuffer();
@ -1313,7 +1312,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
request.startAsync(); request.startAsync();
@ -1344,9 +1343,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{ {
Assumptions.assumeTrue(HttpScheme.HTTP.is(scenario.getScheme())); Assumptions.assumeTrue(HttpScheme.HTTP.is(scenario.getScheme()));
ExecutionException e = assertThrows(ExecutionException.class, ()->{ ExecutionException e = assertThrows(ExecutionException.class, () ->
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 1024); testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 1024));
});
assertThat(e.getCause(), instanceOf(BadMessageException.class)); assertThat(e.getCause(), instanceOf(BadMessageException.class));
assertThat(e.getCause().getMessage(), containsString("Unknown content")); assertThat(e.getCause().getMessage(), containsString("Unknown content"));
@ -1356,9 +1354,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(NonSslScenarioProvider.class) @ArgumentsSource(NonSslScenarioProvider.class)
public void testBigContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario) throws Exception public void testBigContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario) throws Exception
{ {
ExecutionException e = assertThrows(ExecutionException.class, ()->{ ExecutionException e = assertThrows(ExecutionException.class, () ->
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 128 * 1024); testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 128 * 1024));
});
assertThat(e.getCause(), instanceOf(BadMessageException.class)); assertThat(e.getCause(), instanceOf(BadMessageException.class));
assertThat(e.getCause().getMessage(), containsString("Unknown content")); assertThat(e.getCause().getMessage(), containsString("Unknown content"));
@ -1392,7 +1389,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
// Send Connection: close to avoid that the server chunks the content with HTTP 1.1. // Send Connection: close to avoid that the server chunks the content with HTTP 1.1.
@ -1428,7 +1425,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
int count = requests.incrementAndGet(); int count = requests.incrementAndGet();
if (count == maxRetries) if (count == maxRetries)
@ -1457,7 +1454,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
ServletOutputStream output = response.getOutputStream(); ServletOutputStream output = response.getOutputStream();
@ -1507,14 +1504,16 @@ public class HttpClientTest extends AbstractHttpClientServerTest
startServer(scenario, new AbstractHandler() startServer(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
} }
}); });
final AtomicBoolean open = new AtomicBoolean(); final AtomicBoolean open = new AtomicBoolean();
client = new HttpClient(new HttpClientTransportOverHTTP() ClientConnector clientConnector = new ClientConnector();
clientConnector.setSslContextFactory(scenario.newClientSslContextFactory());
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)
{ {
@Override @Override
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise) protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
@ -1529,7 +1528,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
} }
}; };
} }
}, scenario.newSslContextFactory()); });
client.start(); client.start();
final CountDownLatch latch = new CountDownLatch(2); final CountDownLatch latch = new CountDownLatch(2);
@ -1613,10 +1612,11 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void test_IPv6_Host(Scenario scenario) throws Exception public void test_IPv6_Host(Scenario scenario) throws Exception
{ {
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
response.setContentType("text/plain"); response.setContentType("text/plain");
@ -1691,7 +1691,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler() start(scenario, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
assertThat(request.getHeader("Host"), Matchers.notNullValue()); assertThat(request.getHeader("Host"), Matchers.notNullValue());
@ -1717,7 +1717,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
try (ServerSocket server = new ServerSocket(0)) try (ServerSocket server = new ServerSocket(0))
{ {
int idleTimeout = 2000; int idleTimeout = 2000;
startClient(scenario, null, httpClient -> startClient(scenario, httpClient ->
{ {
httpClient.setMaxConnectionsPerDestination(1); httpClient.setMaxConnectionsPerDestination(1);
httpClient.setIdleTimeout(idleTimeout); httpClient.setIdleTimeout(idleTimeout);

View File

@ -18,13 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -38,7 +31,6 @@ import java.nio.charset.StandardCharsets;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -50,17 +42,28 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.Net;
import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.URIUtil;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientURITest extends AbstractHttpClientServerTest public class HttpClientURITest extends AbstractHttpClientServerTest
{ {
@ParameterizedTest @ParameterizedTest
@ArgumentsSource(ScenarioProvider.class) @ArgumentsSource(ScenarioProvider.class)
public void testIPv6Host(Scenario scenario) throws Exception public void testIPv6Host(Scenario scenario) throws Exception
{ {
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
start(scenario, new EmptyServerHandler()); start(scenario, new EmptyServerHandler());
String host = "::1"; String host = "::1";
@ -607,7 +610,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
while (true) while (true)
{ {
String line = reader.readLine(); String line = reader.readLine();
if (line == null || line.isEmpty()) if (StringUtil.isEmpty(line))
break; break;
} }

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Random; import java.util.Random;
@ -29,7 +26,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -37,7 +33,6 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.http.HttpChannelOverHTTP; import org.eclipse.jetty.client.http.HttpChannelOverHTTP;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
@ -46,9 +41,11 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientUploadDuringServerShutdown public class HttpClientUploadDuringServerShutdown
{ {
/** /**
@ -67,7 +64,7 @@ public class HttpClientUploadDuringServerShutdown
server.setHandler(new AbstractHandler() server.setHandler(new AbstractHandler()
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
@ -105,7 +102,7 @@ public class HttpClientUploadDuringServerShutdown
{ {
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(2), null); HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(2));
client.setMaxConnectionsPerDestination(2); client.setMaxConnectionsPerDestination(2);
client.setIdleTimeout(10000); client.setIdleTimeout(10000);
client.setExecutor(clientThreads); client.setExecutor(clientThreads);
@ -144,7 +141,7 @@ public class HttpClientUploadDuringServerShutdown
server.setHandler(new AbstractHandler() server.setHandler(new AbstractHandler()
{ {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{ {
baseRequest.setHandled(true); baseRequest.setHandled(true);
endPointRef.set(baseRequest.getHttpChannel().getEndPoint()); endPointRef.set(baseRequest.getHttpChannel().getEndPoint());
@ -212,7 +209,7 @@ public class HttpClientUploadDuringServerShutdown
} }
}; };
} }
}, null); });
client.setIdleTimeout(10000); client.setIdleTimeout(10000);
client.setExecutor(clientThreads); client.setExecutor(clientThreads);
client.start(); client.start();
@ -252,7 +249,7 @@ public class HttpClientUploadDuringServerShutdown
assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination("http", "localhost", connector.getLocalPort());
DuplexConnectionPool pool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool pool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, pool.getConnectionCount()); assertEquals(0, pool.getConnectionCount());
assertEquals(0, pool.getIdleConnections().size()); assertEquals(0, pool.getIdleConnections().size());

View File

@ -18,10 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
@ -39,7 +35,6 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.ByteBufferContentProvider; import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
@ -51,12 +46,16 @@ import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
{ {
@Override @Override
public HttpClient newHttpClient(Scenario scenario, HttpClientTransport transport) public HttpClient newHttpClient(HttpClientTransport transport)
{ {
HttpClient client = super.newHttpClient(scenario, transport); HttpClient client = super.newHttpClient(transport);
client.setStrictEventOrdering(false); client.setStrictEventOrdering(false);
return client; return client;
} }
@ -69,7 +68,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -120,7 +119,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -172,7 +171,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Queue<Connection> idleConnections = connectionPool.getIdleConnections(); final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
@ -234,7 +233,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -308,7 +307,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -351,7 +350,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -400,7 +399,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -448,7 +447,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -481,7 +480,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost"; String host = "localhost";
int port = connector.getLocalPort(); int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), host, port); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections(); final Collection<Connection> idleConnections = connectionPool.getIdleConnections();

View File

@ -18,12 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -39,7 +33,6 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.ByteBufferContentProvider; import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
@ -47,6 +40,12 @@ import org.eclipse.jetty.util.log.StacklessLogging;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpRequestAbortTest extends AbstractHttpClientServerTest public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{ {
@ParameterizedTest @ParameterizedTest
@ -112,7 +111,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause()); assertSame(cause, x.getCause());
assertFalse(begin.get()); assertFalse(begin.get());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
@ -156,7 +155,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause()); assertSame(cause, x.getCause());
assertFalse(committed.await(1, TimeUnit.SECONDS)); assertFalse(committed.await(1, TimeUnit.SECONDS));
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
@ -200,7 +199,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause()); assertSame(cause, x.getCause());
assertFalse(committed.await(1, TimeUnit.SECONDS)); assertFalse(committed.await(1, TimeUnit.SECONDS));
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
@ -235,7 +234,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
if (aborted.get()) if (aborted.get())
assertSame(cause, x.getCause()); assertSame(cause, x.getCause());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
@ -293,7 +292,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
if (aborted.get()) if (aborted.get())
assertSame(cause, x.getCause()); assertSame(cause, x.getCause());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
@ -353,7 +352,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());
@ -460,7 +459,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause()); assertSame(cause, x.getCause());
} }
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort()); HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size()); assertEquals(0, connectionPool.getActiveConnections().size());

View File

@ -30,7 +30,7 @@ public class InsufficientThreadsDetectionTest
public void testInsufficientThreads() public void testInsufficientThreads()
{ {
QueuedThreadPool clientThreads = new QueuedThreadPool(1); QueuedThreadPool clientThreads = new QueuedThreadPool(1);
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1), null); HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1));
httpClient.setExecutor(clientThreads); httpClient.setExecutor(clientThreads);
assertThrows(IllegalStateException.class, httpClient::start); assertThrows(IllegalStateException.class, httpClient::start);
} }
@ -39,14 +39,14 @@ public class InsufficientThreadsDetectionTest
public void testInsufficientThreadsForMultipleHttpClients() throws Exception public void testInsufficientThreadsForMultipleHttpClients() throws Exception
{ {
QueuedThreadPool clientThreads = new QueuedThreadPool(3); QueuedThreadPool clientThreads = new QueuedThreadPool(3);
HttpClient httpClient1 = new HttpClient(new HttpClientTransportOverHTTP(1), null); HttpClient httpClient1 = new HttpClient(new HttpClientTransportOverHTTP(1));
httpClient1.setExecutor(clientThreads); httpClient1.setExecutor(clientThreads);
httpClient1.start(); httpClient1.start();
assertThrows(IllegalStateException.class, () -> assertThrows(IllegalStateException.class, () ->
{ {
// Share the same thread pool with another instance. // Share the same thread pool with another instance.
HttpClient httpClient2 = new HttpClient(new HttpClientTransportOverHTTP(1), null); HttpClient httpClient2 = new HttpClient(new HttpClientTransportOverHTTP(1));
httpClient2.setExecutor(clientThreads); httpClient2.setExecutor(clientThreads);
httpClient2.start(); httpClient2.start();
}); });

View File

@ -93,7 +93,7 @@ public class LivelockTest
int count = 5; int count = 5;
HttpClientTransport transport = new HttpClientTransportOverHTTP(1); HttpClientTransport transport = new HttpClientTransportOverHTTP(1);
client = new HttpClient(transport, null); client = new HttpClient(transport);
client.setMaxConnectionsPerDestination(2 * count); client.setMaxConnectionsPerDestination(2 * count);
client.setMaxRequestsQueuedPerDestination(2 * count); client.setMaxRequestsQueuedPerDestination(2 * count);
client.setSocketAddressResolver(new SocketAddressResolver.Sync()); client.setSocketAddressResolver(new SocketAddressResolver.Sync());

View File

@ -18,12 +18,13 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import org.eclipse.jetty.toolchain.test.Net;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class ProxyConfigurationTest public class ProxyConfigurationTest
{ {
@Test @Test
@ -70,6 +71,7 @@ public class ProxyConfigurationTest
@Test @Test
public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception
{ {
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
HttpProxy proxy = new HttpProxy("host", 0); HttpProxy proxy = new HttpProxy("host", 0);
proxy.getIncludedAddresses().add("[1::2:3:4]"); proxy.getIncludedAddresses().add("[1::2:3:4]");
proxy.getExcludedAddresses().add("[1::2:3:4]:5"); proxy.getExcludedAddresses().add("[1::2:3:4]:5");

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -30,14 +28,14 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ServerConnectionCloseTest public class ServerConnectionCloseTest
{ {
private HttpClient client; private HttpClient client;
@ -46,7 +44,7 @@ public class ServerConnectionCloseTest
{ {
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(new HttpClientTransportOverHTTP(1), null); client = new HttpClient(new HttpClientTransportOverHTTP(1));
client.setExecutor(clientThreads); client.setExecutor(clientThreads);
client.start(); client.start();
} }
@ -149,7 +147,7 @@ public class ServerConnectionCloseTest
Thread.sleep(1000); Thread.sleep(1000);
// Connection should have been removed from pool. // Connection should have been removed from pool.
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port); HttpDestination destination = (HttpDestination)client.getDestination("http", "localhost", port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount()); assertEquals(0, connectionPool.getIdleConnectionCount());

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client; package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -33,30 +31,37 @@ import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TLSServerConnectionCloseTest public class TLSServerConnectionCloseTest
{ {
private HttpClient client; private HttpClient client;
private void startClient() throws Exception private void startClient() throws Exception
{ {
SslContextFactory sslContextFactory = new SslContextFactory(); ClientConnector clientConnector = new ClientConnector();
sslContextFactory.setEndpointIdentificationAlgorithm(""); clientConnector.setSelectors(1);
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setEndpointIdentificationAlgorithm(null);
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyStorePassword("storepwd");
clientConnector.setSslContextFactory(sslContextFactory);
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(new HttpClientTransportOverHTTP(1), sslContextFactory); clientConnector.setExecutor(clientThreads);
client.setExecutor(clientThreads);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start(); client.start();
} }
@ -168,7 +173,7 @@ public class TLSServerConnectionCloseTest
Thread.sleep(1000); Thread.sleep(1000);
// Connection should have been removed from pool. // Connection should have been removed from pool.
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", port); HttpDestination destination = (HttpDestination)client.getDestination("http", "localhost", port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount()); assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount()); assertEquals(0, connectionPool.getIdleConnectionCount());

View File

@ -42,13 +42,13 @@ import org.junit.jupiter.params.provider.ArgumentsSource;
public class ValidatingConnectionPoolTest extends AbstractHttpClientServerTest public class ValidatingConnectionPoolTest extends AbstractHttpClientServerTest
{ {
@Override @Override
public HttpClient newHttpClient(Scenario scenario, HttpClientTransport transport) public HttpClient newHttpClient(HttpClientTransport transport)
{ {
long timeout = 1000; long timeout = 1000;
transport.setConnectionPoolFactory(destination -> transport.setConnectionPoolFactory(destination ->
new ValidatingConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, destination.getHttpClient().getScheduler(), timeout)); new ValidatingConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, destination.getHttpClient().getScheduler(), timeout));
return super.newHttpClient(scenario, transport); return super.newHttpClient(transport);
} }
@ParameterizedTest @ParameterizedTest

View File

@ -26,8 +26,10 @@ import java.util.function.Supplier;
import org.eclipse.jetty.client.AbstractHttpClientServerTest; import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.ConnectionPool; import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.DuplexConnectionPool; import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.EmptyServerHandler; import org.eclipse.jetty.client.EmptyServerHandler;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
@ -56,7 +58,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{ {
start(scenario, new EmptyServerHandler()); start(scenario, new EmptyServerHandler());
try(HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))) try(HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", connector.getLocalPort())))
{ {
destination.start(); destination.start();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
@ -76,7 +78,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{ {
start(scenario, new EmptyServerHandler()); start(scenario, new EmptyServerHandler());
try(HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))) try(HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", connector.getLocalPort())))
{ {
destination.start(); destination.start();
@ -102,7 +104,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
final CountDownLatch idleLatch = new CountDownLatch(1); final CountDownLatch idleLatch = new CountDownLatch(1);
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())) HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", connector.getLocalPort()))
{ {
@Override @Override
protected ConnectionPool newConnectionPool(HttpClient client) protected ConnectionPool newConnectionPool(HttpClient client)
@ -126,30 +128,29 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
}; };
} }
}; };
{
destination.start();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Connection connection1 = connectionPool.acquire();
// Make sure we entered idleCreated(). destination.start();
assertTrue(idleLatch.await(5, TimeUnit.SECONDS)); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Connection connection1 = connectionPool.acquire();
// There are no available existing connections, so acquire() // Make sure we entered idleCreated().
// returns null because we delayed idleCreated() above assertTrue(idleLatch.await(5, TimeUnit.SECONDS));
assertNull(connection1);
// Second attempt also returns null because we delayed idleCreated() above. // There are no available existing connections, so acquire()
Connection connection2 = connectionPool.acquire(); // returns null because we delayed idleCreated() above
assertNull(connection2); assertNull(connection1);
latch.countDown(); // Second attempt also returns null because we delayed idleCreated() above.
Connection connection2 = connectionPool.acquire();
assertNull(connection2);
// There must be 2 idle connections. latch.countDown();
Connection connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection); // There must be 2 idle connections.
connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS); Connection connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection); assertNotNull(connection);
} connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection);
} }
@ParameterizedTest @ParameterizedTest
@ -158,7 +159,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{ {
start(scenario, new EmptyServerHandler()); start(scenario, new EmptyServerHandler());
try(HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))) try(HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", connector.getLocalPort())))
{ {
destination.start(); destination.start();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
@ -185,9 +186,9 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{ {
startServer(scenario, new EmptyServerHandler()); startServer(scenario, new EmptyServerHandler());
long idleTimeout = 1000; long idleTimeout = 1000;
startClient(scenario, null, httpClient -> httpClient.setIdleTimeout(idleTimeout)); startClient(scenario, httpClient -> httpClient.setIdleTimeout(idleTimeout));
try (HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))) try(HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", connector.getLocalPort())))
{ {
destination.start(); destination.start();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool(); DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();

View File

@ -18,15 +18,6 @@
package org.eclipse.jetty.client.http; package org.eclipse.jetty.client.http;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.EOFException; import java.io.EOFException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
@ -35,15 +26,17 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpRequest; import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponseException; import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
@ -54,10 +47,19 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpReceiverOverHTTPTest public class HttpReceiverOverHTTPTest
{ {
private HttpClient client; private HttpClient client;
private HttpDestinationOverHTTP destination; private HttpDestination destination;
private ByteArrayEndPoint endPoint; private ByteArrayEndPoint endPoint;
private HttpConnectionOverHTTP connection; private HttpConnectionOverHTTP connection;
@ -77,7 +79,7 @@ public class HttpReceiverOverHTTPTest
client = new HttpClient(); client = new HttpClient();
client.setHttpCompliance(compliance); client.setHttpCompliance(compliance);
client.start(); client.start();
destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
endPoint = new ByteArrayEndPoint(); endPoint = new ByteArrayEndPoint();
connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>()); connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>());

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.client.http; package org.eclipse.jetty.client.http;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -28,7 +25,9 @@ import java.util.Locale;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
@ -39,11 +38,13 @@ import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.Promise;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpSenderOverHTTPTest public class HttpSenderOverHTTPTest
{ {
private HttpClient client; private HttpClient client;
@ -65,7 +66,7 @@ public class HttpSenderOverHTTPTest
public void test_Send_NoRequestContent() throws Exception public void test_Send_NoRequestContent() throws Exception
{ {
ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));
@ -99,7 +100,7 @@ public class HttpSenderOverHTTPTest
public void test_Send_NoRequestContent_IncompleteFlush() throws Exception public void test_Send_NoRequestContent_IncompleteFlush() throws Exception
{ {
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));
@ -129,7 +130,7 @@ public class HttpSenderOverHTTPTest
ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
// Shutdown output to trigger the exception on write // Shutdown output to trigger the exception on write
endPoint.shutdownOutput(); endPoint.shutdownOutput();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));
@ -159,7 +160,7 @@ public class HttpSenderOverHTTPTest
public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception
{ {
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));
@ -195,7 +196,7 @@ public class HttpSenderOverHTTPTest
public void test_Send_SmallRequestContent_InOneBuffer() throws Exception public void test_Send_SmallRequestContent_InOneBuffer() throws Exception
{ {
ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));
@ -230,7 +231,7 @@ public class HttpSenderOverHTTPTest
public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception
{ {
ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));
@ -266,7 +267,7 @@ public class HttpSenderOverHTTPTest
public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception
{ {
ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpDestination destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start(); destination.start();
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>()); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<Connection>());
Request request = client.newRequest(URI.create("http://localhost/")); Request request = client.newRequest(URI.create("http://localhost/"));

View File

@ -18,11 +18,6 @@
package org.eclipse.jetty.client.ssl; package org.eclipse.jetty.client.ssl;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -34,7 +29,9 @@ import javax.net.ssl.SSLSession;
import org.eclipse.jetty.client.EmptyServerHandler; import org.eclipse.jetty.client.EmptyServerHandler;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -43,9 +40,13 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* In order to work, client authentication needs a certificate * In order to work, client authentication needs a certificate
* signed by a CA that also signed the server certificate. * signed by a CA that also signed the server certificate.
@ -59,7 +60,7 @@ public class NeedWantClientAuthTest
private ServerConnector connector; private ServerConnector connector;
private HttpClient client; private HttpClient client;
private void startServer(SslContextFactory sslContextFactory, Handler handler) throws Exception private void startServer(SslContextFactory.Server sslContextFactory, Handler handler) throws Exception
{ {
QueuedThreadPool serverThreads = new QueuedThreadPool(); QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server"); serverThreads.setName("server");
@ -72,19 +73,21 @@ public class NeedWantClientAuthTest
server.start(); server.start();
} }
private void startClient(SslContextFactory sslContextFactory) throws Exception private void startClient(SslContextFactory.Client sslContextFactory) throws Exception
{ {
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
QueuedThreadPool clientThreads = new QueuedThreadPool(); QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client"); clientThreads.setName("client");
client = new HttpClient(sslContextFactory); clientConnector.setExecutor(clientThreads);
client.setExecutor(clientThreads); clientConnector.setSslContextFactory(sslContextFactory);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start(); client.start();
} }
private SslContextFactory createSslContextFactory() private SslContextFactory.Server createServerSslContextFactory()
{ {
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setEndpointIdentificationAlgorithm("");
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyStorePassword("storepwd");
return sslContextFactory; return sslContextFactory;
@ -102,11 +105,11 @@ public class NeedWantClientAuthTest
@Test @Test
public void testWantClientAuthWithoutAuth() throws Exception public void testWantClientAuthWithoutAuth() throws Exception
{ {
SslContextFactory serverSSL = createSslContextFactory(); SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setWantClientAuth(true); serverSSL.setWantClientAuth(true);
startServer(serverSSL, new EmptyServerHandler()); startServer(serverSSL, new EmptyServerHandler());
SslContextFactory clientSSL = new SslContextFactory(true); SslContextFactory.Client clientSSL = new SslContextFactory.Client(true);
startClient(clientSSL); startClient(clientSSL);
ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort()) ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort())
@ -119,7 +122,7 @@ public class NeedWantClientAuthTest
@Test @Test
public void testWantClientAuthWithAuth() throws Exception public void testWantClientAuthWithAuth() throws Exception
{ {
SslContextFactory serverSSL = createSslContextFactory(); SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setWantClientAuth(true); serverSSL.setWantClientAuth(true);
startServer(serverSSL, new EmptyServerHandler()); startServer(serverSSL, new EmptyServerHandler());
CountDownLatch handshakeLatch = new CountDownLatch(1); CountDownLatch handshakeLatch = new CountDownLatch(1);
@ -143,7 +146,7 @@ public class NeedWantClientAuthTest
} }
}); });
SslContextFactory clientSSL = new SslContextFactory(true); SslContextFactory.Client clientSSL = new SslContextFactory.Client(true);
clientSSL.setKeyStorePath("src/test/resources/client_keystore.jks"); clientSSL.setKeyStorePath("src/test/resources/client_keystore.jks");
clientSSL.setKeyStorePassword("storepwd"); clientSSL.setKeyStorePassword("storepwd");
startClient(clientSSL); startClient(clientSSL);
@ -166,11 +169,11 @@ public class NeedWantClientAuthTest
// The server still sends bad_certificate to the client, but the client handshake has already // The server still sends bad_certificate to the client, but the client handshake has already
// completed successfully its TLS handshake. // completed successfully its TLS handshake.
SslContextFactory serverSSL = createSslContextFactory(); SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setNeedClientAuth(true); serverSSL.setNeedClientAuth(true);
startServer(serverSSL, new EmptyServerHandler()); startServer(serverSSL, new EmptyServerHandler());
SslContextFactory clientSSL = new SslContextFactory(true); SslContextFactory.Client clientSSL = new SslContextFactory.Client(true);
startClient(clientSSL); startClient(clientSSL);
CountDownLatch handshakeLatch = new CountDownLatch(1); CountDownLatch handshakeLatch = new CountDownLatch(1);
client.addBean(new SslHandshakeListener() client.addBean(new SslHandshakeListener()
@ -210,7 +213,7 @@ public class NeedWantClientAuthTest
@Test @Test
public void testNeedClientAuthWithAuth() throws Exception public void testNeedClientAuthWithAuth() throws Exception
{ {
SslContextFactory serverSSL = createSslContextFactory(); SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setNeedClientAuth(true); serverSSL.setNeedClientAuth(true);
startServer(serverSSL, new EmptyServerHandler()); startServer(serverSSL, new EmptyServerHandler());
CountDownLatch handshakeLatch = new CountDownLatch(1); CountDownLatch handshakeLatch = new CountDownLatch(1);
@ -234,7 +237,7 @@ public class NeedWantClientAuthTest
} }
}); });
SslContextFactory clientSSL = new SslContextFactory(true); SslContextFactory.Client clientSSL = new SslContextFactory.Client(true);
clientSSL.setKeyStorePath("src/test/resources/client_keystore.jks"); clientSSL.setKeyStorePath("src/test/resources/client_keystore.jks");
clientSSL.setKeyStorePassword("storepwd"); clientSSL.setKeyStorePassword("storepwd");
startClient(clientSSL); startClient(clientSSL);

View File

@ -38,9 +38,11 @@ import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -61,7 +63,7 @@ public class SslBytesClientTest extends SslBytesTest
{ {
private ExecutorService threadPool; private ExecutorService threadPool;
private HttpClient client; private HttpClient client;
private SslContextFactory sslContextFactory; private SslContextFactory.Client sslContextFactory;
private SSLServerSocket acceptor; private SSLServerSocket acceptor;
private SimpleProxy proxy; private SimpleProxy proxy;
@ -70,8 +72,11 @@ public class SslBytesClientTest extends SslBytesTest
{ {
threadPool = Executors.newCachedThreadPool(); threadPool = Executors.newCachedThreadPool();
sslContextFactory = new SslContextFactory(true); ClientConnector clientConnector = new ClientConnector();
client = new HttpClient(sslContextFactory); clientConnector.setSelectors(1);
sslContextFactory = new SslContextFactory.Client(true);
clientConnector.setSslContextFactory(sslContextFactory);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.setMaxConnectionsPerDestination(1); client.setMaxConnectionsPerDestination(1);
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
@ -243,7 +248,7 @@ public class SslBytesClientTest extends SslBytesTest
// Trigger a read to have the server write the final renegotiation steps // Trigger a read to have the server write the final renegotiation steps
server.setSoTimeout(100); server.setSoTimeout(100);
assertThrows(SocketTimeoutException.class, ()->serverInput.read()); assertThrows(SocketTimeoutException.class, () -> serverInput.read());
// Renegotiation Handshake // Renegotiation Handshake
record = proxy.readFromServer(); record = proxy.readFromServer();

View File

@ -100,7 +100,7 @@ public class SslBytesServerTest extends SslBytesTest
private final int idleTimeout = 2000; private final int idleTimeout = 2000;
private ExecutorService threadPool; private ExecutorService threadPool;
private Server server; private Server server;
private SslContextFactory sslContextFactory; private SslContextFactory.Server sslContextFactory;
private int serverPort; private int serverPort;
private SSLContext sslContext; private SSLContext sslContext;
private SimpleProxy proxy; private SimpleProxy proxy;
@ -119,7 +119,7 @@ public class SslBytesServerTest extends SslBytesTest
serverEndPoint.set(null); serverEndPoint.set(null);
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
sslContextFactory = new SslContextFactory(); sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyStorePassword("storepwd");

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client.ssl; package org.eclipse.jetty.client.ssl;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.File; import java.io.File;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -36,16 +34,17 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SslConnectionTest public class SslConnectionTest
{ {
@Test @Test
public void testSslConnectionClosedBeforeFill() throws Exception public void testSslConnectionClosedBeforeFill() throws Exception
{ {
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
SslContextFactory sslContextFactory = new SslContextFactory(); SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.start(); sslContextFactory.start();

View File

@ -23,7 +23,7 @@
<argLine> <argLine>
@{argLine} ${jetty.surefire.argLine} @{argLine} ${jetty.surefire.argLine}
--add-modules org.eclipse.jetty.jmx --add-modules org.eclipse.jetty.jmx
--add-reads org.eclipse.jetty.deploy=org.eclipse.jetty.http --add-reads org.eclipse.jetty.deploy=org.eclipse.jetty.http,jetty.servlet.api
</argLine> </argLine>
</configuration> </configuration>
</plugin> </plugin>

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- =============================================================== --> <!-- =============================================================== -->
<!-- Create the deployment manager --> <!-- Create the deployment manager -->

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