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
*.debug
*.dump
.attach_pid*
# vim
.*.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") {
agent { node { label 'linux' } }
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.
*
@ -97,7 +116,7 @@ def mavenBuild(jdk, cmdline, mvnName, junitPublishDisabled) {
mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) {
// 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 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") {
// 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 localRepo = "${env.JENKINS_HOME}/${env.EXECUTOR_NUMBER}"
def settingsName = 'oss-settings.xml'
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") {
git url: "https://github.com/$owner/jetty.project.git", branch: "$branch"
}
stage("Compile") {
withEnv(mvnEnv) {
timeout(time: 15, unit: 'MINUTES') {
withMaven(
maven: mvnName,
jdk: "jdk11",
publisherStrategy: 'EXPLICIT',
globalMavenSettingsConfig: settingsName,
mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) {
sh "mvn -V -B clean install -DskipTests -T6 -e"
}
timeout(time: 15, unit: 'MINUTES') {
withMaven(
maven: mvnName,
jdk: jdk,
publisherStrategy: 'EXPLICIT',
globalMavenSettingsConfig: settingsName,
mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) {
sh "mvn -V -B clean install -DskipTests -T6 -e -pl :jetty-jmh -am"
}
stash name: 'perf-tests', includes: 'jetty-jmh/target/benchmarks.jar'
}
stash name: 'perf-tests', includes: jmhJarPath
}
}
// jmh run
stage("jmh-run") {
node( 'jmh-build-node' ) {
timeout( time: 120, unit: 'MINUTES' ) {
withEnv( ["JAVA_HOME=${tool "jdk8"}"] ) {
node( 'jmh-build-node' ) {
stage("jmh-run") {
timeout( time: 180, unit: 'MINUTES' ) {
withEnv( ["JAVA_HOME=${tool "$jdk"}"] ) {
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"
jmhReport 'jetty-jmh/target/jmh_result.json'
sh "rm -rf jmh_results"
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
terms of the Eclipse Public License 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0, or the Apache Software License
2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0.
terms of the Eclipse Public License 1.0 which is available at
https://www.eclipse.org/org/documents/epl-1.0/EPL-1.0.txt
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-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
+ 113 Add support for NCSA Extended Log File Format
+ 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
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
+ 3097 Duplicated programmatic Servlet Listeners causing duplicate calls
+ 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
+ 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
+ 300 Implement Deflater / Inflater Object Pool
+ 307 Monitor contention in AbstractNCSARequestLog
@ -182,13 +252,6 @@ jetty-9.4.12.v20180830 - 30 August 2018
+ 2860 Leakage of HttpDestinations in HttpClient
+ 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
+ 2777 Workaround for Conscrypt's ssl == null

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<?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
@ -9,7 +9,7 @@ org.eclipse.jetty.servlet.WebApplicationContext object
-->
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get class="org.eclipse.jetty.util.log.Log" name="rootLogger">
<Call name="warn"><Arg>async-rest webapp is deployed. DO NOT USE IN PRODUCTION!</Arg></Call>
<Get name="servletContext">
<Call name="log"><Arg>The async-rest webapp is deployed. DO NOT USE IN PRODUCTION!</Arg><Arg></Arg></Call>
</Get>
</Configure>

View File

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

View File

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

View File

@ -60,10 +60,6 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.PushCacheFilter;
import org.eclipse.jetty.util.ssl.SslContextFactory;
/* ------------------------------------------------------------ */
/**
*/
public class Http2Server
{
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");
if (!new File(jetty_distro).exists())
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.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

View File

@ -99,7 +99,7 @@ public class LikeJettyXml
Server server = new Server(threadPool);
// Scheduler
server.addBean(new ScheduledExecutorScheduler());
server.addBean(new ScheduledExecutorScheduler(null,false));
// HTTP Configuration
HttpConfiguration http_config = new HttpConfiguration();
@ -139,7 +139,7 @@ public class LikeJettyXml
// === jetty-https.xml ===
// 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.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

View File

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

View File

@ -79,7 +79,7 @@ public class ManyHandlers
ServletException
{
Map<String, String[]> params = request.getParameterMap();
if (params.size() > 0)
if (!params.isEmpty())
{
response.setContentType("text/plain");
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.server.Server;
import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
import org.eclipse.jetty.webapp.Configurations;
import org.eclipse.jetty.webapp.WebAppContext;
@ -65,7 +64,7 @@ public class OneWebApp
Configurations.setServerDefault(server);
// Start things up!
// Start things up!
server.start();
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.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketMapping;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
/**
* Example of setting up a Jetty WebSocket server
@ -53,10 +51,10 @@ public class WebSocketServer
* Servlet layer
*/
@SuppressWarnings("serial")
public static class EchoServlet extends WebSocketServlet
public static class EchoServlet extends JettyWebSocketServlet
{
@Override
public void configure(WebSocketServletFactory factory)
public void configure(JettyWebSocketServletFactory factory)
{
factory.addMapping(factory.parsePathSpec("/"), (req,res)->new EchoSocket());
}

View File

@ -1,5 +1,5 @@
<?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">

View File

@ -1,5 +1,5 @@
<?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">

View File

@ -1,5 +1,5 @@
<?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">
<Set name="handler">

View File

@ -19,6 +19,14 @@
<build>
<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>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>

View File

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

View File

@ -111,4 +111,12 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
}
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;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import javax.net.ssl.SSLEngine;
import org.conscrypt.Conscrypt;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
import org.eclipse.jetty.io.Connection;
@ -40,7 +38,7 @@ public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
@Override
public void init()
{
if (Security.getProvider("Conscrypt")==null)
if (Security.getProvider("Conscrypt") == null)
{
Security.addProvider(new OpenSSLProvider());
if (LOG.isDebugEnabled())
@ -59,11 +57,9 @@ public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
{
try
{
Method setAlpnProtocols = sslEngine.getClass().getDeclaredMethod("setApplicationProtocols", String[].class);
setAlpnProtocols.setAccessible(true);
ALPNClientConnection alpn = (ALPNClientConnection)connection;
String[] protocols = alpn.getProtocols().toArray(new String[0]);
setAlpnProtocols.invoke(sslEngine, (Object)protocols);
Conscrypt.setApplicationProtocols(sslEngine, protocols);
((SslConnection.DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
.addHandshakeListener(new ALPNListener(alpn));
}
@ -92,9 +88,9 @@ public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
try
{
SSLEngine sslEngine = alpnConnection.getSSLEngine();
Method method = sslEngine.getClass().getDeclaredMethod("getApplicationProtocol");
method.setAccessible(true);
String protocol = (String)method.invoke(sslEngine);
String protocol = Conscrypt.getApplicationProtocol(sslEngine);
if (LOG.isDebugEnabled())
LOG.debug("Selected {} for {}", protocol, alpnConnection);
alpnConnection.selected(protocol);
}
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>
<scope>test</scope>
</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>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Description>Conscrypt ALPN</Bundle-Description>
<Import-Package>org.conscrypt;version="${conscrypt.version}",*</Import-Package>
<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>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Description>Conscrypt ALPN</Bundle-Description>
<Import-Package>org.conscrypt;version="${conscrypt.version}",*</Import-Package>
<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>
<_nouses>true</_nouses>
</instructions>
</configuration>
</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>
</build>
</project>

View File

@ -18,13 +18,13 @@
package org.eclipse.jetty.alpn.conscrypt.server;
import java.lang.reflect.Method;
import java.security.Security;
import java.util.List;
import java.util.function.BiFunction;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import org.conscrypt.ApplicationProtocolSelector;
import org.conscrypt.Conscrypt;
import org.conscrypt.OpenSSLProvider;
import org.eclipse.jetty.alpn.server.ALPNServerConnection;
import org.eclipse.jetty.io.Connection;
@ -41,7 +41,7 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
@Override
public void init()
{
if (Security.getProvider("Conscrypt")==null)
if (Security.getProvider("Conscrypt") == null)
{
Security.addProvider(new OpenSSLProvider());
if (LOG.isDebugEnabled())
@ -56,13 +56,11 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
}
@Override
public void configure(SSLEngine sslEngine,Connection connection)
public void configure(SSLEngine sslEngine, Connection connection)
{
try
{
Method method = sslEngine.getClass().getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class);
method.setAccessible(true);
method.invoke(sslEngine,new ALPNCallback((ALPNServerConnection)connection));
Conscrypt.setApplicationProtocolSelector(sslEngine, new ALPNCallback((ALPNServerConnection)connection));
}
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 ALPNCallback(ALPNServerConnection connection)
{
alpnConnection = connection;
alpnConnection = connection;
((DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this);
}
@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);
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
@ -99,7 +105,7 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled())
LOG.debug("TLS handshake succeeded, protocol={} for {}", protocol, alpnConnection);
if (protocol ==null)
if (protocol == null)
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);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(h1.getProtocol());
connector = new ServerConnector(server, newSslContextFactory(), alpn, h1, h2);
connector = new ServerConnector(server, newServerSslContextFactory(), alpn, h1, h2);
server.addConnector(connector);
server.setHandler(handler);
server.start();
@ -72,13 +72,12 @@ public class JDK9ALPNTest
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.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
sslContextFactory.setIncludeProtocols("TLSv1.2");
// The mandatory HTTP/2 cipher.
sslContextFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
return sslContextFactory;
@ -96,7 +95,7 @@ public class JDK9ALPNTest
}
});
SslContextFactory sslContextFactory = new SslContextFactory(true);
SslContextFactory sslContextFactory = new SslContextFactory.Client(true);
sslContextFactory.start();
SSLContext sslContext = sslContextFactory.getSslContext();
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();
SSLContext sslContext = sslContextFactory.getSslContext();
try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort()))

View File

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

View File

@ -1,5 +1,5 @@
<?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">

View File

@ -18,14 +18,6 @@
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.FileOutputStream;
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.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)
public class TestAnnotationParser
{
@ -142,7 +143,7 @@ public class TestAnnotationParser
if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation))
return;
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);
}
}

View File

@ -52,6 +52,7 @@ import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.servlet.Source;
import org.eclipse.jetty.util.log.Log;
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.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppClassLoader;
@ -592,7 +593,7 @@ public class AntWebAppContext extends WebAppContext
TaskLog.logWithTimestamp("Starting web application "+this.getDescriptor());
if (jettyEnvXml != null && jettyEnvXml.exists())
getConfiguration(EnvConfiguration.class).setJettyEnvXml(Resource.toURL(jettyEnvXml));
getConfiguration(EnvConfiguration.class).setJettyEnvResource(new PathResource(jettyEnvXml));
ClassLoader parentLoader = this.getClass().getClassLoader();
if (parentLoader instanceof AntClassLoader)
@ -608,7 +609,7 @@ public class AntWebAppContext extends WebAppContext
//apply a context xml file if one was supplied
if (contextXml != null)
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(contextXml));
XmlConfiguration xmlConfiguration = new XmlConfiguration(new PathResource(contextXml));
TaskLog.log("Applying context xml file "+contextXml);
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.HandlerCollection;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.xml.sax.SAXException;
@ -451,7 +452,7 @@ public class ServerProxyImpl implements ServerProxy
XmlConfiguration configuration;
try
{
configuration = new XmlConfiguration(Resource.toURL(jettyXml));
configuration = new XmlConfiguration(new PathResource(jettyXml));
configuration.configure(server);
}
catch (MalformedURLException e)

View File

@ -186,7 +186,17 @@
</dependency>
<dependency>
<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>
</dependency>
<dependency>

View File

@ -1,5 +1,5 @@
<?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 -->

View File

@ -1,22 +1,22 @@
<?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">
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.util.Decorator</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg>
</Call>
<Call name="prependServerClass">
<Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg>
</Call>
<Get name="serverClasspathPattern">
<Call name="add">
<Arg>-org.eclipse.jetty.util.Decorator</Arg>
</Call>
<Call name="add">
<Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg>
</Call>
<Call name="add">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg>
</Call>
<Call name="add">
<Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg>
</Call>
<Call name="add">
<Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg>
</Call>
</Get>
</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>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-client</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<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.api;
exports org.eclipse.jetty.client.dynamic;
exports org.eclipse.jetty.client.http;
exports org.eclipse.jetty.client.jmx to org.eclipse.jetty.jmx;
exports org.eclipse.jetty.client.proxy;
exports org.eclipse.jetty.client.util;
requires org.eclipse.jetty.http;
@ -30,6 +32,8 @@ module org.eclipse.jetty.client
// Only required if using SPNEGO.
requires static java.security.jgss;
// Only required if using the dynamic transport.
requires static org.eclipse.jetty.alpn.client;
// Only required if using JMX.
requires static org.eclipse.jetty.jmx;
}

View File

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

View File

@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -217,6 +218,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
path = request.getPath();
}
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)
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.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -61,6 +62,7 @@ import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Fields;
@ -122,19 +124,16 @@ public class HttpClient extends ContainerLifeCycle
public static final String USER_AGENT = "Jetty/" + Jetty.VERSION;
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 List<Request.Listener> requestListeners = new ArrayList<>();
private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
private final HttpClientTransport transport;
private final SslContextFactory sslContextFactory;
private final ClientConnector connector;
private AuthenticationStore authenticationStore = new HttpAuthenticationStore();
private CookieManager cookieManager;
private CookieStore cookieStore;
private Executor executor;
private ByteBufferPool byteBufferPool;
private Scheduler scheduler;
private SocketAddressResolver resolver;
private HttpField agentField = new HttpField(HttpHeader.USER_AGENT, USER_AGENT);
private boolean followRedirects = true;
@ -143,48 +142,28 @@ public class HttpClient extends ContainerLifeCycle
private int requestBufferSize = 4096;
private int responseBufferSize = 16384;
private int maxRedirects = 8;
private SocketAddress bindAddress;
private long connectTimeout = 15000;
private long addressResolutionTimeout = 15000;
private long idleTimeout;
private boolean tcpNoDelay = true;
private boolean strictEventOrdering = false;
private HttpField encodingField;
private boolean removeIdleDestinations = false;
private boolean connectBlocking = false;
private String name = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
private HttpCompliance httpCompliance = HttpCompliance.RFC7230;
private String defaultRequestContentType = "application/octet-stream";
/**
* Creates a HttpClient instance that can perform requests to non-TLS destinations only
* (that is, requests with the "http" scheme only, and not "https").
*
* @see #HttpClient(SslContextFactory) to perform requests to TLS destinations.
* Creates a HttpClient instance that can perform HTTP/1.1 requests to non-TLS and TLS destinations.
*/
public HttpClient()
{
this(null);
this(new HttpClientTransportOverHTTP());
}
/**
* 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)
public HttpClient(HttpClientTransport transport)
{
this(new HttpClientTransportOverHTTP(), sslContextFactory);
}
public HttpClient(HttpClientTransport transport, SslContextFactory sslContextFactory)
{
this.transport = transport;
this.transport = Objects.requireNonNull(transport);
addBean(transport);
this.sslContextFactory = sslContextFactory;
addBean(sslContextFactory);
this.connector = ((AbstractHttpClientTransport)transport).getBean(ClientConnector.class);
addBean(handlers);
addBean(decoderFactories);
}
@ -202,34 +181,34 @@ public class HttpClient extends ContainerLifeCycle
/**
* @return the {@link SslContextFactory} that manages TLS encryption
* @see #HttpClient(SslContextFactory)
*/
public SslContextFactory getSslContextFactory()
public SslContextFactory.Client getSslContextFactory()
{
return sslContextFactory;
return connector.getSslContextFactory();
}
@Override
protected void doStart() throws Exception
{
Executor executor = getExecutor();
if (executor == null)
{
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName(name);
setExecutor(threadPool);
}
ByteBufferPool byteBufferPool = getByteBufferPool();
if (byteBufferPool == null)
setByteBufferPool(new MappedByteBufferPool(2048,
executor instanceof ThreadPool.SizedThreadPool
? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2
: ProcessorUtils.availableProcessors() * 2));
Scheduler scheduler = getScheduler();
if (scheduler == null)
setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false));
if (resolver == null)
setSocketAddressResolver(new SocketAddressResolver.Async(executor, scheduler, getAddressResolutionTimeout()));
setSocketAddressResolver(new SocketAddressResolver.Async(getExecutor(), getScheduler(), getAddressResolutionTimeout()));
handlers.put(new ContinueProtocolHandler());
handlers.put(new RedirectProtocolHandler(this));
@ -291,6 +270,8 @@ public class HttpClient extends ContainerLifeCycle
*/
public void setCookieStore(CookieStore cookieStore)
{
if (isStarted())
throw new IllegalStateException();
this.cookieStore = Objects.requireNonNull(cookieStore);
this.cookieManager = newCookieManager();
}
@ -319,6 +300,8 @@ public class HttpClient extends ContainerLifeCycle
*/
public void setAuthenticationStore(AuthenticationStore authenticationStore)
{
if (isStarted())
throw new IllegalStateException();
this.authenticationStore = authenticationStore;
}
@ -523,10 +506,11 @@ public class HttpClient extends ContainerLifeCycle
*/
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) &&
!HttpScheme.WS.is(scheme) && !HttpScheme.WSS.is(scheme))
@ -536,13 +520,18 @@ public class HttpClient extends ContainerLifeCycle
host = host.toLowerCase(Locale.ENGLISH);
port = normalizePort(scheme, port);
Origin origin = new Origin(scheme, host, port);
HttpDestination destination = destinations.get(origin);
return new Origin(scheme, host, port);
}
private HttpDestination resolveDestination(HttpDestination.Key key)
{
HttpDestination destination = destinations.get(key);
if (destination == null)
{
destination = transport.newHttpDestination(origin);
destination = getTransport().newHttpDestination(key);
// Start the destination before it's published to other threads.
addManaged(destination);
HttpDestination existing = destinations.putIfAbsent(origin, destination);
HttpDestination existing = destinations.putIfAbsent(key, destination);
if (existing != null)
{
removeBean(destination);
@ -560,7 +549,7 @@ public class HttpClient extends ContainerLifeCycle
protected boolean removeDestination(HttpDestination 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)
{
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);
}
@ -636,7 +634,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public ByteBufferPool getByteBufferPool()
{
return byteBufferPool;
return connector.getByteBufferPool();
}
/**
@ -644,10 +642,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public void setByteBufferPool(ByteBufferPool byteBufferPool)
{
if (isStarted())
LOG.warn("Calling setByteBufferPool() while started is deprecated");
updateBean(this.byteBufferPool, byteBufferPool);
this.byteBufferPool = byteBufferPool;
connector.setByteBufferPool(byteBufferPool);
}
/**
@ -677,7 +672,7 @@ public class HttpClient extends ContainerLifeCycle
@ManagedAttribute("The timeout, in milliseconds, for connect() operations")
public long getConnectTimeout()
{
return connectTimeout;
return connector.getConnectTimeout().toMillis();
}
/**
@ -686,7 +681,7 @@ public class HttpClient extends ContainerLifeCycle
*/
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")
public long getIdleTimeout()
{
return idleTimeout;
return connector.getIdleTimeout().toMillis();
}
/**
@ -726,7 +721,7 @@ public class HttpClient extends ContainerLifeCycle
*/
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()
{
return bindAddress;
return connector.getBindAddress();
}
/**
@ -745,7 +740,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public void setBindAddress(SocketAddress bindAddress)
{
this.bindAddress = bindAddress;
connector.setBindAddress(bindAddress);
}
/**
@ -790,7 +785,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public Executor getExecutor()
{
return executor;
return connector.getExecutor();
}
/**
@ -798,10 +793,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public void setExecutor(Executor executor)
{
if (isStarted())
LOG.warn("Calling setExecutor() while started is deprecated");
updateBean(this.executor, executor);
this.executor = executor;
connector.setExecutor(executor);
}
/**
@ -809,7 +801,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public Scheduler getScheduler()
{
return scheduler;
return connector.getScheduler();
}
/**
@ -817,10 +809,7 @@ public class HttpClient extends ContainerLifeCycle
*/
public void setScheduler(Scheduler scheduler)
{
if (isStarted())
LOG.warn("Calling setScheduler() while started is deprecated");
updateBean(this.scheduler, scheduler);
this.scheduler = scheduler;
connector.setScheduler(scheduler);
}
/**
@ -837,7 +826,7 @@ public class HttpClient extends ContainerLifeCycle
public void setSocketAddressResolver(SocketAddressResolver resolver)
{
if (isStarted())
LOG.warn("Calling setSocketAddressResolver() while started is deprecated");
throw new IllegalStateException();
updateBean(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)
*/
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)
*/
public void setMaxRedirects(int maxRedirects)
@ -1059,7 +1048,7 @@ public class HttpClient extends ContainerLifeCycle
@ManagedAttribute("Whether the connect() operation is blocking")
public boolean isConnectBlocking()
{
return connectBlocking;
return connector.isConnectBlocking();
}
/**
@ -1074,7 +1063,7 @@ public class HttpClient extends ContainerLifeCycle
*/
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)
{
if (host != null && host.matches("\\[.*\\]"))
if (host != null && host.matches("\\[.*]"))
return host.substring(1, host.length() - 1);
return host;
}

View File

@ -43,7 +43,7 @@ public interface HttpClientTransport extends ClientConnectionFactory
* Sets the {@link HttpClient} instance on this transport.
* <p>
* 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}.
*
* @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
* 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
*/
public HttpDestination newHttpDestination(Origin origin);
public HttpDestination newHttpDestination(HttpDestination.Key key);
/**
* 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
*/
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
*/
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 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.Request;
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.Logger;
public abstract class HttpConnection implements Connection
public abstract class HttpConnection implements IConnection
{
private static final Logger LOG = Log.getLogger(HttpConnection.class);
@ -80,8 +79,6 @@ public abstract class HttpConnection implements Connection
httpRequest.abort(result.failure);
}
protected abstract SendFailure send(HttpExchange exchange);
protected void normalizeRequest(Request request)
{
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.util.AttributesMap;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class HttpConversation extends AttributesMap
{
private static final Logger LOG = Log.getLogger(HttpConversation.class);
private final Deque<HttpExchange> exchanges = new ConcurrentLinkedDeque<>();
private volatile List<Response.ResponseListener> listeners;
@ -118,6 +122,7 @@ public class HttpConversation extends AttributesMap
HttpExchange lastExchange = exchanges.peekLast();
if (firstExchange == lastExchange)
{
// We don't have a conversation, just a single request.
if (overrideListener != null)
listeners.add(overrideListener);
else
@ -125,13 +130,16 @@ public class HttpConversation extends AttributesMap
}
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());
if (overrideListener != null)
listeners.add(overrideListener);
else
listeners.addAll(firstExchange.getResponseListeners());
}
if (LOG.isDebugEnabled())
LOG.debug("Exchanges in conversation {}, override={}, listeners={}", exchanges.size(), overrideListener, listeners);
this.listeners = listeners;
}

View File

@ -23,20 +23,25 @@ import java.io.IOException;
import java.nio.channels.AsynchronousCloseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
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.HttpHeader;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.CyclicTimeout;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HostPort;
@ -52,12 +57,12 @@ import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.Sweeper;
@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);
private final HttpClient client;
private final Origin origin;
private final Key key;
private final Queue<HttpExchange> exchanges;
private final RequestNotifier requestNotifier;
private final ResponseNotifier responseNotifier;
@ -67,21 +72,38 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
private final TimeoutTask timeout;
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.origin = origin;
this.key = key;
this.exchanges = newExchangeQueue(client);
this.requestNotifier = new RequestNotifier(client);
this.responseNotifier = new ResponseNotifier();
this.timeout = new TimeoutTask(client.getScheduler());
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
ProxyConfiguration proxyConfig = client.getProxyConfiguration();
proxy = proxyConfig.match(origin);
ClientConnectionFactory connectionFactory = client.getTransport();
this.proxy = proxyConfig.match(getOrigin());
this.connectionFactory = factoryFn.apply(createClientConnectionFactory());
}
private ClientConnectionFactory createClientConnectionFactory()
{
ProxyConfiguration.Proxy proxy = getProxy();
ClientConnectionFactory connectionFactory = getHttpClient().getTransport();
if (proxy != null)
{
connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
@ -93,12 +115,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (isSecure())
connectionFactory = newSslClientConnectionFactory(connectionFactory);
}
this.connectionFactory = connectionFactory;
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
return connectionFactory;
}
@Override
@ -147,9 +164,14 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
return client;
}
public Key getKey()
{
return key;
}
public Origin getOrigin()
{
return origin;
return key.origin;
}
public Queue<HttpExchange> getHttpExchanges()
@ -181,7 +203,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
@ManagedAttribute(value = "The destination scheme", readonly = true)
public String getScheme()
{
return origin.getScheme();
return getOrigin().getScheme();
}
@Override
@ -190,14 +212,14 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
{
// InetSocketAddress.getHostString() transforms the host string
// in case of IPv6 addresses, so we return the original host string
return origin.getAddress().getHost();
return getOrigin().getAddress().getHost();
}
@Override
@ManagedAttribute(value = "The destination port", readonly = true)
public int getPort()
{
return origin.getAddress().getPort();
return getOrigin().getAddress().getPort();
}
@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()
{
return proxy == null ? origin.getAddress() : proxy.getAddress();
return proxy == null ? getOrigin().getAddress() : proxy.getAddress();
}
public HttpField getHostField()
@ -235,7 +257,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
}
protected void send(HttpRequest request, List<Response.ResponseListener> listeners)
{
{
if (!getScheme().equalsIgnoreCase(request.getScheme()))
throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this);
if (!getHost().equalsIgnoreCase(request.getHost()))
@ -343,7 +365,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
}
else
{
SendFailure result = send(connection, exchange);
SendFailure result = ((IConnection)connection).send(exchange);
if (result != null)
{
if (LOG.isDebugEnabled())
@ -358,8 +380,6 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
}
}
protected abstract SendFailure send(Connection connection, HttpExchange exchange);
@Override
public void newConnection(Promise<Connection> promise)
{
@ -476,7 +496,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
public String asString()
{
return origin.asString();
return getKey().asString();
}
@Override
@ -490,8 +510,178 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
exchanges.size(),
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 final AtomicLong nextTimeout = new AtomicLong(Long.MAX_VALUE);
@ -504,10 +694,13 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
@Override
public void onTimeoutExpired()
{
if (LOG.isDebugEnabled())
LOG.debug("{} timeout expired", this);
nextTimeout.set(Long.MAX_VALUE);
long now = System.nanoTime();
long nextExpiresAt = Long.MAX_VALUE;
// Check all queued exchanges for those that have expired
// and to determine when the next check must be.
for (HttpExchange exchange : exchanges)
@ -521,7 +714,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
else if (expiresAt < nextExpiresAt)
nextExpiresAt = expiresAt;
}
if (nextExpiresAt < Long.MAX_VALUE && client.isRunning())
schedule(nextExpiresAt);
}
@ -536,12 +729,16 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (timeoutAt != expiresAt)
{
long delay = expiresAt - System.nanoTime();
if (LOG.isDebugEnabled())
LOG.debug("Scheduled timeout in {} ms", TimeUnit.NANOSECONDS.toMillis(delay));
if (delay <= 0)
{
onTimeoutExpired();
}
else
{
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.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -61,10 +62,10 @@ public class HttpRedirector
{
private static final Logger LOG = Log.getLogger(HttpRedirector.class);
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
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 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.
*
* @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
* @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 ExecutionException if the redirect failed
* @throws ExecutionException if the redirect failed
* @see #redirect(Request, Response, Response.CompleteListener)
*/
public Result redirect(Request request, Response response) throws InterruptedException, ExecutionException
@ -144,7 +145,7 @@ public class HttpRedirector
/**
* 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 listener the listener that receives response events
* @return the request to the redirected location
@ -292,7 +293,8 @@ public class HttpRedirector
Integer redirects = (Integer)conversation.getAttribute(ATTRIBUTE);
if (redirects == null)
redirects = 0;
if (redirects < client.getMaxRedirects())
int maxRedirects = client.getMaxRedirects();
if (maxRedirects < 0 || redirects < maxRedirects)
{
++redirects;
conversation.setAttribute(ATTRIBUTE, redirects);
@ -310,19 +312,17 @@ public class HttpRedirector
try
{
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
redirect.method(method);
redirect.onRequestBegin(new Request.BeginListener()
redirect.onRequestBegin(request ->
{
@Override
public void onBegin(Request redirect)
{
Throwable cause = httpRequest.getAbortCause();
if (cause != null)
redirect.abort(cause);
}
Throwable cause = httpRequest.getAbortCause();
if (cause != null)
request.abort(cause);
});
redirect.send(listener);

View File

@ -18,10 +18,9 @@
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)
{
super(client, origin);
}
public SendFailure send(HttpExchange exchange);
}

View File

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

View File

@ -18,13 +18,33 @@
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()
{
ConnectionPool connectionPool = getConnectionPool();

View File

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

View File

@ -73,7 +73,7 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
@Override
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();
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.Result;
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.Logger;
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);
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)
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();
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))
{
long delay = timeoutAt - System.nanoTime();
if (LOG.isDebugEnabled())
LOG.debug("Scheduled timeout in {} ms for {}", TimeUnit.NANOSECONDS.toMillis(delay), request);
if (delay <= 0)
{
onTimeoutExpired();
}
else
{
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);
/**
* @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();
/**
* @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
* @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.DuplexConnectionPool;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
@ -53,9 +53,9 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
}
@Override
public HttpDestination newHttpDestination(Origin origin)
public HttpDestination newHttpDestination(HttpDestination.Key key)
{
return new HttpDestinationOverHTTP(getHttpClient(), origin);
return new DuplexHttpDestination(getHttpClient(), key);
}
@Override

View File

@ -28,6 +28,7 @@ import java.util.concurrent.atomic.LongAdder;
import org.eclipse.jetty.client.HttpConnection;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.IConnection;
import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
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.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);
@ -71,9 +72,9 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
return channel;
}
public HttpDestinationOverHTTP getHttpDestination()
public HttpDestination getHttpDestination()
{
return (HttpDestinationOverHTTP)delegate.getHttpDestination();
return delegate.getHttpDestination();
}
@Override
@ -116,7 +117,8 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
delegate.send(request, listener);
}
protected SendFailure send(HttpExchange exchange)
@Override
public SendFailure send(HttpExchange exchange)
{
return delegate.send(exchange);
}
@ -238,7 +240,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
}
@Override
protected SendFailure send(HttpExchange exchange)
public SendFailure send(HttpExchange exchange)
{
Request request = exchange.getRequest();
normalizeRequest(request);

View File

@ -221,6 +221,13 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
return 4096;
}
@Override
public boolean isHeaderCacheCaseSensitive()
{
// TODO get from configuration
return false;
}
@Override
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.nio.charset.StandardCharsets;
import java.util.Base64;
import org.eclipse.jetty.client.HttpClient;
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.http.HttpHeader;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.B64Code;
/**
* Implementation of the HTTP "Basic" authentication defined in RFC 2617.
@ -91,7 +91,8 @@ public class BasicAuthentication extends AbstractAuthentication
{
this.uri = uri;
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

View File

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

View File

@ -18,11 +18,6 @@
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.InterruptedIOException;
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.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.StringContentProvider;
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.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
{
@ParameterizedTest
@ -87,7 +86,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
ContentResponse response = client.newRequest(host, port)
@ -124,7 +123,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
CountDownLatch resultLatch = new CountDownLatch(1);
@ -185,7 +184,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8));
@ -240,7 +239,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
ContentResponse response = client.newRequest(host, port)

View File

@ -18,9 +18,6 @@
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.util.ArrayList;
import java.util.List;
@ -31,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
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.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
public class ConnectionPoolTest
{
@ -83,7 +82,7 @@ public class ConnectionPoolTest
transport.setConnectionPoolFactory(factory);
server.start();
client = new HttpClient(transport, null);
client = new HttpClient(transport);
client.start();
}
@ -115,7 +114,7 @@ public class ConnectionPoolTest
start(factory, new EmptyServerHandler()
{
@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()))
{

View File

@ -18,10 +18,6 @@
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.util.concurrent.CountDownLatch;
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.Result;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@Disabled
public class ExternalSiteTest
{
@ -44,7 +44,7 @@ public class ExternalSiteTest
@BeforeEach
public void prepare() throws Exception
{
client = new HttpClient(new SslContextFactory());
client = new HttpClient();
client.start();
}
@ -54,6 +54,7 @@ public class ExternalSiteTest
client.stop();
}
@Tag("external")
@Test
public void testExternalSite() throws Exception
{
@ -64,39 +65,29 @@ public class ExternalSiteTest
assumeCanConnectTo(host, port);
final CountDownLatch latch1 = new CountDownLatch(1);
client.newRequest(host, port).send(new Response.CompleteListener()
client.newRequest(host, port).send(result ->
{
@Override
public void onComplete(Result result)
{
if (!result.isFailed() && result.getResponse().getStatus() == 200)
latch1.countDown();
}
assertTrue(result.isSucceeded());
assertEquals(200, result.getResponse().getStatus());
latch1.countDown();
});
assertTrue(latch1.await(10, TimeUnit.SECONDS));
assertTrue(latch1.await(15, TimeUnit.SECONDS));
// Try again the same URI, but without specifying the port
final CountDownLatch latch2 = new CountDownLatch(1);
client.newRequest("http://" + host).send(new Response.CompleteListener()
client.newRequest("http://" + host).send(result ->
{
@Override
public void onComplete(Result result)
{
assertTrue(result.isSucceeded());
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
public void testExternalSSLSite() throws Exception
{
client.stop();
client = new HttpClient(new SslContextFactory());
client.start();
String host = "api-3t.paypal.com";
int port = 443;
@ -104,18 +95,16 @@ public class ExternalSiteTest
assumeCanConnectTo(host, port);
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
public void onComplete(Result result)
{
if (result.isSucceeded() && result.getResponse().getStatus() == 200)
latch.countDown();
}
assertTrue(result.isSucceeded());
assertEquals(200, result.getResponse().getStatus());
latch.countDown();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
assertTrue(latch.await(15, TimeUnit.SECONDS));
}
@Tag("external")
@Test
public void testExternalSiteWrongProtocol() throws Exception
{
@ -129,14 +118,7 @@ public class ExternalSiteTest
{
final CountDownLatch latch = new CountDownLatch(3);
client.newRequest(host, port)
.onResponseFailure(new Response.FailureListener()
{
@Override
public void onFailure(Response response, Throwable failure)
{
latch.countDown();
}
})
.onResponseFailure((response, failure) -> latch.countDown())
.send(new Response.Listener.Adapter()
{
@Override
@ -152,10 +134,11 @@ public class ExternalSiteTest
latch.countDown();
}
});
assertTrue(latch.await(10, TimeUnit.SECONDS));
assertTrue(latch.await(15, TimeUnit.SECONDS));
}
}
@Tag("external")
@Test
public void testExternalSiteRedirect() throws Exception
{
@ -168,6 +151,7 @@ public class ExternalSiteTest
ContentResponse response = client.newRequest(host, port)
.scheme(HttpScheme.HTTPS.asString())
.path("/twitter")
.timeout(15, TimeUnit.SECONDS)
.send();
assertEquals(200, response.getStatus());
}
@ -180,7 +164,7 @@ public class ExternalSiteTest
}
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;
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.security.cert.CertificateException;
import java.util.concurrent.ExecutionException;
import javax.net.ssl.SSLHandshakeException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
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.Request;
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.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
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
* section 3.1) is configurable in SslContextFactory and works as expected.
@ -52,7 +52,7 @@ import org.junit.jupiter.api.Test;
@Disabled
public class HostnameVerificationTest
{
private SslContextFactory clientSslContextFactory = new SslContextFactory();
private SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client();
private Server server;
private HttpClient client;
private NetworkConnector connector;
@ -64,7 +64,7 @@ public class HostnameVerificationTest
serverThreads.setName("server");
server = new Server(serverThreads);
SslContextFactory serverSslContextFactory = new SslContextFactory();
SslContextFactory.Server serverSslContextFactory = new SslContextFactory.Server();
serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
serverSslContextFactory.setKeyStorePassword("storepwd");
connector = new ServerConnector(server, serverSslContextFactory);
@ -72,7 +72,7 @@ public class HostnameVerificationTest
server.setHandler(new DefaultHandler()
{
@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);
response.getWriter().write("foobar");
@ -80,14 +80,19 @@ public class HostnameVerificationTest
});
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.setKeyStorePassword("storepwd");
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(clientSslContextFactory);
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
client = new HttpClient(clientSslContextFactory);
client.setExecutor(clientThreads);
clientConnector.setExecutor(clientThreads);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start();
}
@ -112,9 +117,7 @@ public class HostnameVerificationTest
clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
String uri = "https://localhost:" + connector.getLocalPort() + "/";
ExecutionException x = assertThrows(ExecutionException.class, ()->{
client.GET(uri);
});
ExecutionException x = assertThrows(ExecutionException.class, ()-> client.GET(uri));
Throwable cause = x.getCause();
assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class));
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.Listener;
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.DeferredContentProvider;
import org.eclipse.jetty.client.util.DigestAuthentication;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.security.Authenticator;
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.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.Attributes;
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.provider.ArgumentsSource;
import static org.eclipse.jetty.client.api.Authentication.ANY_REALM;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -137,7 +139,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
{
startBasic(scenario, new EmptyServerHandler());
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
@ -155,7 +157,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
{
startDigest(scenario, new EmptyServerHandler());
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
@ -227,16 +229,19 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_BasicAuthentication_ThenRedirect(Scenario scenario) throws Exception
{
startBasic(scenario, new AbstractHandler()
startBasic(scenario, new EmptyServerHandler()
{
private final AtomicInteger requests = new AtomicInteger();
@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 (requests.incrementAndGet() == 1)
response.sendRedirect(URIUtil.newURI(scenario.getScheme(), request.getServerName(), request.getServerPort(), request.getRequestURI(), null));
int r = requests.incrementAndGet();
if (r == 1)
{
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)
public void test_Redirect_ThenBasicAuthentication(Scenario scenario) throws Exception
{
startBasic(scenario, new AbstractHandler()
startBasic(scenario, new EmptyServerHandler()
{
@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"))
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));
}
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);
private final IntFunction<ByteBuffer> generator;
private GeneratingContentProvider(IntFunction<ByteBuffer> generator)
String authType = "Authenticate";
start(scenario, new EmptyServerHandler()
{
this.generator = generator;
}
@Override
public long getLength()
{
return -1;
}
@Override
public boolean isReproducible()
{
return true;
}
@Override
public Iterator<ByteBuffer> iterator()
{
return new Iterator<ByteBuffer>()
@Override
protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
{
private int index;
public ByteBuffer current;
// Always reply with a 401 to see if the client
// can handle an infinite authentication loop.
response.setStatus(HttpStatus.UNAUTHORIZED_401);
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), authType);
}
});
@Override
@SuppressWarnings("ReferenceEquality")
public boolean hasNext()
AuthenticationStore authenticationStore = client.getAuthenticationStore();
URI uri = URI.create(scenario.getScheme() + "://localhost:" + connector.getLocalPort());
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++);
if (current == null)
current = DONE;
return uri;
}
return current != DONE;
}
@Override
public ByteBuffer next()
{
ByteBuffer result = current;
current = null;
if (result == null)
throw new NoSuchElementException();
return result;
}
};
}
@Override
public void apply(Request request)
{
}
};
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.send();
assertEquals(HttpStatus.UNAUTHORIZED_401, response.getStatus());
}
@Test
@ -801,4 +801,61 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
assertEquals("thermostat", headerInfo.getParameter("realm"));
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;
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.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.Request;
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.http.HttpStatus;
import org.eclipse.jetty.util.FuturePromise;
import org.junit.jupiter.params.ParameterizedTest;
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
{
@ParameterizedTest
@ -59,7 +58,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
assertNotNull(response);
assertEquals(200, response.getStatus());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
HttpDestination httpDestination = (HttpDestination)destination;
DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool();
assertTrue(connectionPool.getActiveConnections().isEmpty());
assertTrue(connectionPool.getIdleConnections().isEmpty());
@ -94,7 +93,7 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection;
assertFalse(httpConnection.getEndPoint().isOpen());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
HttpDestination httpDestination = (HttpDestination)destination;
DuplexConnectionPool connectionPool = (DuplexConnectionPool)httpDestination.getConnectionPool();
assertTrue(connectionPool.getActiveConnections().isEmpty());
assertTrue(connectionPool.getIdleConnections().isEmpty());

View File

@ -18,10 +18,6 @@
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.util.concurrent.CountDownLatch;
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.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach;
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
{
private Server server;
@ -75,7 +74,7 @@ public class HttpClientFailureTest
startServer(new EmptyServerHandler());
final AtomicReference<HttpConnectionOverHTTP> connectionRef = new AtomicReference<>();
client = new HttpClient(new HttpClientTransportOverHTTP()
client = new HttpClient(new HttpClientTransportOverHTTP(1)
{
@Override
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
@ -84,15 +83,14 @@ public class HttpClientFailureTest
connectionRef.set(connection);
return connection;
}
}, null);
});
client.start();
assertThrows(ExecutionException.class, ()->{
client.newRequest("localhost", connector.getLocalPort())
.onRequestHeaders(request -> connectionRef.get().getEndPoint().close())
.timeout(5, TimeUnit.SECONDS)
.send();
});
assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort())
.onRequestHeaders(request -> connectionRef.get().getEndPoint().close())
.timeout(5, TimeUnit.SECONDS)
.send());
DuplexConnectionPool connectionPool = (DuplexConnectionPool)connectionRef.get().getHttpDestination().getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
@ -106,7 +104,7 @@ public class HttpClientFailureTest
startServer(new EmptyServerHandler());
final AtomicReference<HttpConnectionOverHTTP> connectionRef = new AtomicReference<>();
client = new HttpClient(new HttpClientTransportOverHTTP()
client = new HttpClient(new HttpClientTransportOverHTTP(1)
{
@Override
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
@ -115,7 +113,7 @@ public class HttpClientFailureTest
connectionRef.set(connection);
return connection;
}
}, null);
});
client.start();
final CountDownLatch commitLatch = new CountDownLatch(1);
@ -155,98 +153,4 @@ public class HttpClientFailureTest
assertEquals(0, connectionPool.getActiveConnections().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.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
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.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.B64Code;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
@ -82,7 +81,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
{
final String user = "foo";
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 realm = "test_realm";
final int status = HttpStatus.NO_CONTENT_204;
@ -162,7 +161,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
{
String user = "foo";
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 serverHost = "server";
int serverPort = HttpScheme.HTTP.is(scenario.getScheme()) ? 80 : 443;

View File

@ -18,14 +18,6 @@
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.net.URLDecoder;
import java.nio.ByteBuffer;
@ -34,7 +26,9 @@ import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.ServletException;
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.HttpStatus;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.IO;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
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
{
@ParameterizedTest
@ -128,14 +129,13 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
{
start(scenario, new RedirectHandler());
ExecutionException x = assertThrows(ExecutionException.class, ()->{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.method(HttpMethod.DELETE)
.path("/301/localhost/done")
.timeout(5, TimeUnit.SECONDS)
.send();
});
ExecutionException x = assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.method(HttpMethod.DELETE)
.path("/301/localhost/done")
.timeout(5, TimeUnit.SECONDS)
.send());
HttpResponseException xx = (HttpResponseException)x.getCause();
Response response = xx.getResponse();
assertNotNull(response);
@ -170,13 +170,12 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
start(scenario, new RedirectHandler());
client.setMaxRedirects(1);
ExecutionException x = assertThrows(ExecutionException.class, ()->{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.path("/303/localhost/302/localhost/done")
.timeout(5, TimeUnit.SECONDS)
.send();
});
ExecutionException x = assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.path("/303/localhost/302/localhost/done")
.timeout(5, TimeUnit.SECONDS)
.send());
HttpResponseException xx = (HttpResponseException)x.getCause();
Response response = xx.getResponse();
assertNotNull(response);
@ -269,12 +268,11 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testRedirectWithWrongScheme(Scenario scenario) throws Exception
{
start(scenario, new AbstractHandler()
start(scenario, new EmptyServerHandler()
{
@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.setHeader("Location", "ssh://localhost:" + connector.getLocalPort() + "/path");
}
@ -439,12 +437,11 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
public void testRedirectWithCorruptedBody(Scenario scenario) throws Exception
{
byte[] bytes = "ok".getBytes(StandardCharsets.UTF_8);
start(scenario, new AbstractHandler()
start(scenario, new EmptyServerHandler()
{
@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"))
{
response.setStatus(HttpStatus.SEE_OTHER_303);
@ -471,6 +468,60 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
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
{
testMethodRedirect(scenario, method, method, redirectCode);
@ -519,10 +570,10 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
assertEquals(200, response.getStatus());
}
private class RedirectHandler extends AbstractHandler
private class RedirectHandler extends EmptyServerHandler
{
@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
{
@ -551,10 +602,6 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
// Echo content back
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 org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
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.SslHandshakeListener;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
@ -66,7 +69,7 @@ public class HttpClientTLSTest
private ServerConnector connector;
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();
serverThreads.setName("server");
@ -79,22 +82,37 @@ public class HttpClientTLSTest
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();
clientThreads.setName("client");
client = new HttpClient(sslContextFactory);
client.setExecutor(clientThreads);
clientConnector.setExecutor(clientThreads);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
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.setKeyStorePassword("storepwd");
return sslContextFactory;
}
@AfterEach
@ -109,7 +127,7 @@ public class HttpClientTLSTest
@Test
public void testNoCommonTLSProtocol() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
serverTLSFactory.setIncludeProtocols("TLSv1.3");
startServer(serverTLSFactory, new EmptyServerHandler());
@ -123,7 +141,7 @@ public class HttpClientTLSTest
}
});
SslContextFactory clientTLSFactory = createSslContextFactory();
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
clientTLSFactory.setIncludeProtocols("TLSv1.2");
startClient(clientTLSFactory);
@ -150,7 +168,7 @@ public class HttpClientTLSTest
@Test
public void testNoCommonTLSCiphers() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
serverTLSFactory.setIncludeCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
startServer(serverTLSFactory, new EmptyServerHandler());
@ -164,7 +182,7 @@ public class HttpClientTLSTest
}
});
SslContextFactory clientTLSFactory = createSslContextFactory();
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
clientTLSFactory.setExcludeCipherSuites(".*_SHA$");
startClient(clientTLSFactory);
@ -191,7 +209,7 @@ public class HttpClientTLSTest
@Test
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnServer() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
// TLS 1.1 protocol, but only TLS 1.2 ciphers.
serverTLSFactory.setIncludeProtocols("TLSv1.1");
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);
CountDownLatch clientLatch = new CountDownLatch(1);
@ -236,7 +254,7 @@ public class HttpClientTLSTest
@Test
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
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.
clientTLSFactory.setIncludeProtocols("TLSv1.1");
clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
@ -278,7 +296,7 @@ public class HttpClientTLSTest
@Test
public void testHandshakeSucceeded() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
CountDownLatch serverLatch = new CountDownLatch(1);
@ -291,7 +309,7 @@ public class HttpClientTLSTest
}
});
SslContextFactory clientTLSFactory = createSslContextFactory();
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
startClient(clientTLSFactory);
CountDownLatch clientLatch = new CountDownLatch(1);
@ -317,7 +335,7 @@ public class HttpClientTLSTest
@Test
public void testHandshakeSucceededWithSessionResumption() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
AtomicReference<byte[]> serverSession = new AtomicReference<>();
@ -330,7 +348,7 @@ public class HttpClientTLSTest
}
});
SslContextFactory clientTLSFactory = createSslContextFactory();
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
startClient(clientTLSFactory);
AtomicReference<byte[]> clientSession = new AtomicReference<>();
@ -397,10 +415,10 @@ public class HttpClientTLSTest
@Test
public void testClientRawCloseDoesNotInvalidateSession() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
SslContextFactory clientTLSFactory = createSslContextFactory();
SslContextFactory clientTLSFactory = createClientSslContextFactory();
clientTLSFactory.start();
String host = "localhost";
@ -452,13 +470,17 @@ public class HttpClientTLSTest
@Test
public void testServerRawCloseDetectedByClient() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory serverTLSFactory = createServerSslContextFactory();
serverTLSFactory.start();
try (ServerSocket server = new ServerSocket(0))
{
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
client = new HttpClient(createSslContextFactory())
clientConnector.setExecutor(clientThreads);
clientConnector.setSslContextFactory(createClientSslContextFactory());
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector))
{
@Override
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
@ -468,7 +490,6 @@ public class HttpClientTLSTest
return ssl;
}
};
client.setExecutor(clientThreads);
client.start();
CountDownLatch latch = new CountDownLatch(1);
@ -488,7 +509,7 @@ public class HttpClientTLSTest
while (true)
{
String line = reader.readLine();
if (line == null || line.isEmpty())
if (StringUtil.isEmpty(line))
break;
}
@ -522,10 +543,10 @@ public class HttpClientTLSTest
@Test
public void testHostNameVerificationFailure() throws Exception
{
SslContextFactory serverTLSFactory = createSslContextFactory();
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
startServer(serverTLSFactory, new EmptyServerHandler());
SslContextFactory clientTLSFactory = createSslContextFactory();
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
// Make sure the host name is not verified at the TLS level.
clientTLSFactory.setEndpointIdentificationAlgorithm(null);
// 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.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
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.http.HttpClientTransportOverHTTP;
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.BytesContentProvider;
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.HttpVersion;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
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.WorkDirExtension;
import org.eclipse.jetty.util.Callback;
@ -111,7 +111,6 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
public WorkDir testdir;
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
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);
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();
long start = System.nanoTime();
@ -187,7 +186,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
baseRequest.setHandled(true);
@ -212,7 +211,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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");
ServletOutputStream output = response.getOutputStream();
@ -245,7 +244,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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");
ServletOutputStream output = response.getOutputStream();
@ -282,7 +281,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
String value = request.getParameter(paramName);
@ -315,7 +314,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
String value = request.getParameter(paramName);
@ -349,7 +348,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
consume(request.getInputStream(), true);
@ -381,7 +380,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
consume(request.getInputStream(), true);
@ -412,7 +411,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
consume(request.getInputStream(), true);
@ -493,7 +492,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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"))
baseRequest.getHttpChannel().getEndPoint().close();
@ -612,7 +611,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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
IO.copy(request.getInputStream(), response.getOutputStream());
@ -634,7 +633,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@Override
public Iterator<ByteBuffer> iterator()
{
return new Iterator<ByteBuffer>()
return new Iterator<>()
{
@Override
public boolean hasNext()
@ -681,7 +680,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
.scheme(scenario.getScheme())
.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();
connectionPool.getActiveConnections().iterator().next().close();
})
@ -706,7 +705,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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
{
@ -722,13 +721,12 @@ public class HttpClientTest extends AbstractHttpClientServerTest
final String host = "localhost";
final int port = connector.getLocalPort();
assertThrows(TimeoutException.class, ()->{
client.newRequest(host, port)
.scheme(scenario.getScheme())
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
.send();
});
assertThrows(TimeoutException.class, () ->
client.newRequest(host, port)
.scheme(scenario.getScheme())
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
.send());
// Make another request without specifying the idle timeout, should not fail
ContentResponse response = client.newRequest(host, port)
@ -744,6 +742,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testSendToIPv6Address(Scenario scenario) throws Exception
{
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
start(scenario, new EmptyServerHandler());
ContentResponse response = client.newRequest("[::1]", connector.getLocalPort())
@ -763,7 +762,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
response.setHeader(headerName, "X");
@ -821,7 +820,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
response.getOutputStream().write(new byte[length]);
@ -882,14 +881,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest
public void testConnectHostWithMultipleAddresses(Scenario scenario) throws Exception
{
startServer(scenario, new EmptyServerHandler());
startClient(scenario, null, client ->
startClient(scenario, client ->
{
client.setSocketAddressResolver(new SocketAddressResolver.Async(client.getExecutor(), client.getScheduler(), 5000)
{
@Override
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
public void succeeded(List<InetSocketAddress> result)
@ -925,7 +924,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent"));
@ -959,7 +958,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
ArrayList<String> userAgents = Collections.list(request.getHeaders("User-Agent"));
@ -1161,7 +1160,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
response.getOutputStream().write(content);
@ -1205,7 +1204,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
assertEquals(host, request.getServerName());
@ -1227,7 +1226,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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
byte[] content = "TEST".getBytes("UTF-8");
@ -1255,7 +1254,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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
response.flushBuffer();
@ -1313,7 +1312,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
request.startAsync();
@ -1344,9 +1343,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
Assumptions.assumeTrue(HttpScheme.HTTP.is(scenario.getScheme()));
ExecutionException e = assertThrows(ExecutionException.class, ()->{
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 1024);
});
ExecutionException e = assertThrows(ExecutionException.class, () ->
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 1024));
assertThat(e.getCause(), instanceOf(BadMessageException.class));
assertThat(e.getCause().getMessage(), containsString("Unknown content"));
@ -1356,9 +1354,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(NonSslScenarioProvider.class)
public void testBigContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario) throws Exception
{
ExecutionException e = assertThrows(ExecutionException.class, ()->{
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 128 * 1024);
});
ExecutionException e = assertThrows(ExecutionException.class, () ->
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 128 * 1024));
assertThat(e.getCause(), instanceOf(BadMessageException.class));
assertThat(e.getCause().getMessage(), containsString("Unknown content"));
@ -1392,7 +1389,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
// 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()
{
@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();
if (count == maxRetries)
@ -1457,7 +1454,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
ServletOutputStream output = response.getOutputStream();
@ -1507,14 +1504,16 @@ public class HttpClientTest extends AbstractHttpClientServerTest
startServer(scenario, new AbstractHandler()
{
@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);
}
});
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
protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise<Connection> promise)
@ -1529,7 +1528,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
};
}
}, scenario.newSslContextFactory());
});
client.start();
final CountDownLatch latch = new CountDownLatch(2);
@ -1613,10 +1612,11 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_IPv6_Host(Scenario scenario) throws Exception
{
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
start(scenario, new AbstractHandler()
{
@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);
response.setContentType("text/plain");
@ -1691,7 +1691,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new AbstractHandler()
{
@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);
assertThat(request.getHeader("Host"), Matchers.notNullValue());
@ -1717,7 +1717,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
try (ServerSocket server = new ServerSocket(0))
{
int idleTimeout = 2000;
startClient(scenario, null, httpClient ->
startClient(scenario, httpClient ->
{
httpClient.setMaxConnectionsPerDestination(1);
httpClient.setIdleTimeout(idleTimeout);

View File

@ -18,13 +18,6 @@
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.IOException;
import java.io.InputStreamReader;
@ -38,7 +31,6 @@ import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
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.HttpStatus;
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.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest;
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
{
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testIPv6Host(Scenario scenario) throws Exception
{
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
start(scenario, new EmptyServerHandler());
String host = "::1";
@ -607,7 +610,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
while (true)
{
String line = reader.readLine();
if (line == null || line.isEmpty())
if (StringUtil.isEmpty(line))
break;
}

View File

@ -18,9 +18,6 @@
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.InputStream;
import java.util.Random;
@ -29,7 +26,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
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.HttpClientTransportOverHTTP;
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.io.EndPoint;
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.util.Promise;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
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
{
/**
@ -67,7 +64,7 @@ public class HttpClientUploadDuringServerShutdown
server.setHandler(new AbstractHandler()
{
@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);
byte[] buffer = new byte[1024];
@ -105,7 +102,7 @@ public class HttpClientUploadDuringServerShutdown
{
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(2), null);
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(2));
client.setMaxConnectionsPerDestination(2);
client.setIdleTimeout(10000);
client.setExecutor(clientThreads);
@ -144,7 +141,7 @@ public class HttpClientUploadDuringServerShutdown
server.setHandler(new AbstractHandler()
{
@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);
endPointRef.set(baseRequest.getHttpChannel().getEndPoint());
@ -212,7 +209,7 @@ public class HttpClientUploadDuringServerShutdown
}
};
}
}, null);
});
client.setIdleTimeout(10000);
client.setExecutor(clientThreads);
client.start();
@ -252,7 +249,7 @@ public class HttpClientUploadDuringServerShutdown
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();
assertEquals(0, pool.getConnectionCount());
assertEquals(0, pool.getIdleConnections().size());

View File

@ -18,10 +18,6 @@
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.nio.ByteBuffer;
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.Response;
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.http.HttpHeader;
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.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
{
@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);
return client;
}
@ -69,7 +68,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -120,7 +119,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -172,7 +171,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
@ -234,7 +233,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -308,7 +307,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -351,7 +350,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -400,7 +399,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -448,7 +447,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
@ -481,7 +480,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
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();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();

View File

@ -18,12 +18,6 @@
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.nio.ByteBuffer;
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.Result;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.server.handler.AbstractHandler;
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.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
{
@ParameterizedTest
@ -112,7 +111,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause());
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -156,7 +155,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause());
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -200,7 +199,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause());
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -235,7 +234,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
if (aborted.get())
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -293,7 +292,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
if (aborted.get())
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -353,7 +352,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -460,7 +459,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());

View File

@ -30,7 +30,7 @@ public class InsufficientThreadsDetectionTest
public void testInsufficientThreads()
{
QueuedThreadPool clientThreads = new QueuedThreadPool(1);
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1), null);
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1));
httpClient.setExecutor(clientThreads);
assertThrows(IllegalStateException.class, httpClient::start);
}
@ -39,14 +39,14 @@ public class InsufficientThreadsDetectionTest
public void testInsufficientThreadsForMultipleHttpClients() throws Exception
{
QueuedThreadPool clientThreads = new QueuedThreadPool(3);
HttpClient httpClient1 = new HttpClient(new HttpClientTransportOverHTTP(1), null);
HttpClient httpClient1 = new HttpClient(new HttpClientTransportOverHTTP(1));
httpClient1.setExecutor(clientThreads);
httpClient1.start();
assertThrows(IllegalStateException.class, () ->
{
// 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.start();
});

View File

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

View File

@ -18,12 +18,13 @@
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.assertTrue;
import org.junit.jupiter.api.Test;
public class ProxyConfigurationTest
{
@Test
@ -70,6 +71,7 @@ public class ProxyConfigurationTest
@Test
public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception
{
Assumptions.assumeTrue(Net.isIpv6InterfaceAvailable());
HttpProxy proxy = new HttpProxy("host", 0);
proxy.getIncludedAddresses().add("[1::2:3:4]");
proxy.getExcludedAddresses().add("[1::2:3:4]:5");

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.io.InputStream;
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.Request;
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.http.HttpStatus;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ServerConnectionCloseTest
{
private HttpClient client;
@ -46,7 +44,7 @@ public class ServerConnectionCloseTest
{
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
client = new HttpClient(new HttpClientTransportOverHTTP(1), null);
client = new HttpClient(new HttpClientTransportOverHTTP(1));
client.setExecutor(clientThreads);
client.start();
}
@ -149,7 +147,7 @@ public class ServerConnectionCloseTest
Thread.sleep(1000);
// 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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount());

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.io.InputStream;
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.Request;
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.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TLSServerConnectionCloseTest
{
private HttpClient client;
private void startClient() throws Exception
{
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setEndpointIdentificationAlgorithm("");
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
sslContextFactory.setEndpointIdentificationAlgorithm(null);
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd");
clientConnector.setSslContextFactory(sslContextFactory);
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("client");
client = new HttpClient(new HttpClientTransportOverHTTP(1), sslContextFactory);
client.setExecutor(clientThreads);
clientConnector.setExecutor(clientThreads);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start();
}
@ -168,7 +173,7 @@ public class TLSServerConnectionCloseTest
Thread.sleep(1000);
// 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();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount());

View File

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

View File

@ -26,8 +26,10 @@ import java.util.function.Supplier;
import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.EmptyServerHandler;
import org.eclipse.jetty.client.HttpClient;
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.ContentResponse;
@ -56,7 +58,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{
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();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
@ -76,7 +78,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{
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();
@ -102,7 +104,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
final CountDownLatch idleLatch = 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
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().
assertTrue(idleLatch.await(5, TimeUnit.SECONDS));
destination.start();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
Connection connection1 = connectionPool.acquire();
// There are no available existing connections, so acquire()
// returns null because we delayed idleCreated() above
assertNull(connection1);
// Make sure we entered idleCreated().
assertTrue(idleLatch.await(5, TimeUnit.SECONDS));
// Second attempt also returns null because we delayed idleCreated() above.
Connection connection2 = connectionPool.acquire();
assertNull(connection2);
// There are no available existing connections, so acquire()
// returns null because we delayed idleCreated() above
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.
Connection connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection);
connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection);
}
latch.countDown();
// There must be 2 idle connections.
Connection connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection);
connection = pollIdleConnection(connectionPool, 5, TimeUnit.SECONDS);
assertNotNull(connection);
}
@ParameterizedTest
@ -158,7 +159,7 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{
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();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
@ -185,9 +186,9 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{
startServer(scenario, new EmptyServerHandler());
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();
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();

View File

@ -18,15 +18,6 @@
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.nio.charset.StandardCharsets;
import java.util.Collections;
@ -35,15 +26,17 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.eclipse.jetty.client.DuplexHttpDestination;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
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.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
{
private HttpClient client;
private HttpDestinationOverHTTP destination;
private HttpDestination destination;
private ByteArrayEndPoint endPoint;
private HttpConnectionOverHTTP connection;
@ -77,7 +79,7 @@ public class HttpReceiverOverHTTPTest
client = new HttpClient();
client.setHttpCompliance(compliance);
client.start();
destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
destination = new DuplexHttpDestination(client, new Origin("http", "localhost", 8080));
destination.start();
endPoint = new ByteArrayEndPoint();
connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter<>());

View File

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

View File

@ -18,11 +18,6 @@
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.util.concurrent.CountDownLatch;
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.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.server.Handler;
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.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
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
* signed by a CA that also signed the server certificate.
@ -59,7 +60,7 @@ public class NeedWantClientAuthTest
private ServerConnector connector;
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();
serverThreads.setName("server");
@ -72,19 +73,21 @@ public class NeedWantClientAuthTest
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();
clientThreads.setName("client");
client = new HttpClient(sslContextFactory);
client.setExecutor(clientThreads);
clientConnector.setExecutor(clientThreads);
clientConnector.setSslContextFactory(sslContextFactory);
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
client.start();
}
private SslContextFactory createSslContextFactory()
private SslContextFactory.Server createServerSslContextFactory()
{
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setEndpointIdentificationAlgorithm("");
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd");
return sslContextFactory;
@ -102,11 +105,11 @@ public class NeedWantClientAuthTest
@Test
public void testWantClientAuthWithoutAuth() throws Exception
{
SslContextFactory serverSSL = createSslContextFactory();
SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setWantClientAuth(true);
startServer(serverSSL, new EmptyServerHandler());
SslContextFactory clientSSL = new SslContextFactory(true);
SslContextFactory.Client clientSSL = new SslContextFactory.Client(true);
startClient(clientSSL);
ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort())
@ -119,7 +122,7 @@ public class NeedWantClientAuthTest
@Test
public void testWantClientAuthWithAuth() throws Exception
{
SslContextFactory serverSSL = createSslContextFactory();
SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setWantClientAuth(true);
startServer(serverSSL, new EmptyServerHandler());
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.setKeyStorePassword("storepwd");
startClient(clientSSL);
@ -166,11 +169,11 @@ public class NeedWantClientAuthTest
// The server still sends bad_certificate to the client, but the client handshake has already
// completed successfully its TLS handshake.
SslContextFactory serverSSL = createSslContextFactory();
SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setNeedClientAuth(true);
startServer(serverSSL, new EmptyServerHandler());
SslContextFactory clientSSL = new SslContextFactory(true);
SslContextFactory.Client clientSSL = new SslContextFactory.Client(true);
startClient(clientSSL);
CountDownLatch handshakeLatch = new CountDownLatch(1);
client.addBean(new SslHandshakeListener()
@ -210,7 +213,7 @@ public class NeedWantClientAuthTest
@Test
public void testNeedClientAuthWithAuth() throws Exception
{
SslContextFactory serverSSL = createSslContextFactory();
SslContextFactory.Server serverSSL = createServerSslContextFactory();
serverSSL.setNeedClientAuth(true);
startServer(serverSSL, new EmptyServerHandler());
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.setKeyStorePassword("storepwd");
startClient(clientSSL);

View File

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

View File

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

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.client.ssl;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.File;
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.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SslConnectionTest
{
@Test
public void testSslConnectionClosedBeforeFill() throws Exception
{
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
SslContextFactory sslContextFactory = new SslContextFactory();
SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.start();

View File

@ -23,7 +23,7 @@
<argLine>
@{argLine} ${jetty.surefire.argLine}
--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>
</configuration>
</plugin>

View File

@ -1,5 +1,5 @@
<?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 -->

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