Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-1743-refactor-maven-plugin-redux

This commit is contained in:
Jan Bartel 2019-08-19 17:03:51 +10:00
commit 60ee8ac8ee
546 changed files with 11453 additions and 8310 deletions

120
Jenkinsfile vendored
View File

@ -11,64 +11,66 @@ pipeline {
agent { node { label 'linux' } }
options { timeout(time: 120, unit: 'MINUTES') }
steps {
mavenBuild("jdk11", "-Pmongodb install", "maven3", true) // -Pautobahn
mavenBuild("jdk11", "-Pmongodb install", "maven3", true) // -Pautobahn
// Collect up the jacoco execution results (only on main build)
jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class',
exclusionPattern: '' +
// build tools
'**/org/eclipse/jetty/ant/**' +
',**/org/eclipse/jetty/maven/**' +
',**/org/eclipse/jetty/jspc/**' +
// example code / documentation
',**/org/eclipse/jetty/embedded/**' +
',**/org/eclipse/jetty/asyncrest/**' +
',**/org/eclipse/jetty/demo/**' +
// special environments / late integrations
',**/org/eclipse/jetty/gcloud/**' +
',**/org/eclipse/jetty/infinispan/**' +
',**/org/eclipse/jetty/osgi/**' +
',**/org/eclipse/jetty/spring/**' +
',**/org/eclipse/jetty/http/spi/**' +
// test classes
',**/org/eclipse/jetty/tests/**' +
',**/org/eclipse/jetty/test/**',
execPattern: '**/target/jacoco.exec',
classPattern: '**/target/classes',
sourcePattern: '**/src/main/java'
junit testResults: '**/target/surefire-reports/*.xml,**/target/autobahntestsuite-reports/*.xml'
exclusionPattern: '' +
// build tools
'**/org/eclipse/jetty/ant/**' +
',**/org/eclipse/jetty/maven/**' +
',**/org/eclipse/jetty/jspc/**' +
// example code / documentation
',**/org/eclipse/jetty/embedded/**' +
',**/org/eclipse/jetty/asyncrest/**' +
',**/org/eclipse/jetty/demo/**' +
// special environments / late integrations
',**/org/eclipse/jetty/gcloud/**' +
',**/org/eclipse/jetty/infinispan/**' +
',**/org/eclipse/jetty/osgi/**' +
',**/org/eclipse/jetty/spring/**' +
',**/org/eclipse/jetty/http/spi/**' +
// test classes
',**/org/eclipse/jetty/tests/**' +
',**/org/eclipse/jetty/test/**',
execPattern: '**/target/jacoco.exec',
classPattern: '**/target/classes',
sourcePattern: '**/src/main/java'
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
maven_invoker reportsFilenamePattern: "**/target/invoker-reports/BUILD*.xml", invokerBuildDir: "**/target/it"
junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml,**/target/autobahntestsuite-reports/*.xml'
}
}
stage("Build / Test - JDK12") {
agent { node { label 'linux' } }
options { timeout(time: 120, unit: 'MINUTES') }
steps {
mavenBuild("jdk12", "-Pmongodb install", "maven3", true)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
maven_invoker reportsFilenamePattern: "**/target/invoker-reports/BUILD*.xml", invokerBuildDir: "**/target/it"
timeout(time: 120, unit: 'MINUTES') {
mavenBuild("jdk12", "-Pmongodb install", "maven3", true)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml'
}
}
}
stage("Build Javadoc") {
agent { node { label 'linux' } }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
mavenBuild("jdk11", "install javadoc:javadoc -DskipTests", "maven3", true)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'JavaDoc'], [parserName: 'Java']]
timeout(time: 30, unit: 'MINUTES') {
mavenBuild("jdk11", "install javadoc:javadoc -DskipTests", "maven3", true)
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'JavaDoc'], [parserName: 'Java']]
}
}
}
stage("Checkstyle ") {
agent { node { label 'linux' } }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
mavenBuild("jdk11", "install checkstyle:check -DskipTests", "maven3", true)
recordIssues(
enabledForFailure: true, aggregatingResults: true,
tools: [java(), checkStyle(pattern: '**/target/checkstyle-result.xml', reportEncoding: 'UTF-8')]
)
timeout(time: 30, unit: 'MINUTES') {
mavenBuild("jdk11", "install -f build-resources", "maven3", true)
mavenBuild("jdk11", "install checkstyle:check -DskipTests", "maven3", true)
recordIssues(
enabledForFailure: true, aggregatingResults: true,
tools: [java(), checkStyle(pattern: '**/target/checkstyle-result.xml', reportEncoding: 'UTF-8')])
}
}
}
}
@ -89,23 +91,21 @@ pipeline {
def slackNotif() {
script {
try
{
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}"
}
} catch (Exception e) {
e.printStackTrace()
echo "skip failure slack notification: " + e.getMessage()
script {
try {
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}"
}
} catch (Exception e) {
e.printStackTrace()
echo "skip failure slack notification: " + e.getMessage()
}
}
}
@ -120,18 +120,16 @@ def slackNotif() {
* @return the Jenkinsfile step representing a maven build
*/
def mavenBuild(jdk, cmdline, mvnName, junitPublishDisabled) {
def localRepo = "${env.JENKINS_HOME}/${env.EXECUTOR_NUMBER}" // ".repository" //
def settingsName = 'oss-settings.xml'
def localRepo = ".repository"
def mavenOpts = '-Xms1g -Xmx4g -Djava.awt.headless=true'
withMaven(
maven: mvnName,
jdk: "$jdk",
publisherStrategy: 'EXPLICIT',
globalMavenSettingsConfig: settingsName,
options: [junitPublisher(disabled: junitPublishDisabled),mavenLinkerPublisher(disabled: false),pipelineGraphPublisher(disabled: false)],
mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) {
maven: mvnName,
jdk: "$jdk",
publisherStrategy: 'EXPLICIT',
options: [junitPublisher(disabled: junitPublishDisabled), mavenLinkerPublisher(disabled: false), pipelineGraphPublisher(disabled: false)],
mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) {
// Some common Maven command line + provided command line
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

@ -1,5 +1,242 @@
jetty-10.0.0-SNAPSHOT
jetty-10.0.0-alpha0 - 11 July 2019
+ 113 Add support for NCSA Extended Log File Format
+ 114 Bring back overlay deployer
+ 132 ClientConnector abstraction
+ 207 Support javax.websocket version 1.1
+ 215 Add Conscrypt for native ALPN/TLS/SSL
+ 300 Implement Deflater / Inflater Object Pool
+ 482 [jetty-osgi] The CCL while parsing the xml files should be set to a
combination of Jetty and Bundle-Classloader
+ 592 Support no-value Host header in HttpParser
+ 632 JMX tests rely on fixed port
+ 675 Slf4jLog.ignore() should produce at DEBUG level
+ 676 JavaUtilLog.ignore() should produce at DEBUG level
+ 677 Logging of .ignore() should indicate that it was an "Ignored Exception"
+ 746 Implement Servlet 4.0 Request.getMappings
+ 801 Jetty respond with status 200 instead of 304 while using Servlet 4.0
PushBuilder
+ 809 NPE in WebInfConfiguration for webapp deploy in osgi
+ 987 Can GzipHandler check if .gz file exists only by some paths?
+ 1135 Avoid allocations from Method.getParameterTypes() if possible
+ 1200 Use PathWatcher in DeploymentManager
+ 1350 Dynamic selection of the transport to use based on ALPN on the client
side
+ 1368 Need to support KeyStore/TrustStore with null passwords
+ 1384 Expose StatisticsServlet to webapp
+ 1468 Configure PKIX Revocation Checker for SslContextFactory
+ 1485 Add systemd service file
+ 1498 Add JRTResource to support future Java 9 classloader behaviors
+ 1499 ClasspathPattern needs MODULE ruleset to support future Java 9
classloader behaviors
+ 1503 IPv6 address needs normalization (without brackets) in
ForwardedRequestCustomizer
+ 1551 Move CookieCutter to jetty-http
+ 1571 Support Hazelcast session management in 9.4
+ 1574 TypeUtilTest#testGetLocationOfClass is user settings dependant
+ 1591 JDBCSessionDataStore doesn't work with root context on Oracle DB
+ 1592 CompressedContentFormat.tagEquals() - incorrect comparison of entity
tag hashes
+ 1595 HTTP/2: Avoid sending unnecessary stream WINDOW_UPDATE frames
+ 1599 WebSocketCloseTest fails
+ 1600 Update jndi.mod and plus.mod
+ 1603 WebSocketServerFactory NPE in toString()
+ 1604 WebSocketContainer stop needs improvement
+ 1605 ContainerProvider.getWebSocketContainer() behavior is not to spec
+ 1615 Password defaults in jetty-ssl-context.xml should be removed
+ 1618 AsyncContext.dispatch() does not use raw/encoded URI
+ 1622 HeaderFilter doesn't work if the response has been committed
+ 1623 JettyRunMojo use dependencies from reactor (outputdirectory)
+ 1625 Support new IANA declared Websocket Close Status Codes
+ 1637 Thread per connection retained in HTTP/2
+ 1638 Add it test for Maven Plugin
+ 1642 Using RewriteHandler with AsyncContext.dispatch() and
HttpServletRequestWrapper not possible
+ 1643 ProxyServlet always uses default number of selector threads -
constructor should allow to overwrite the default.
+ 1645 NotSerializableException: DoSFilter when using Non-Clustered Session
Management: File System
+ 1656 Improve configurability of ConnectionPools
+ 1671 Asymmetric usage of trailers in MetaData.Request
+ 1675 Session id should not be logged with INFO level in AbstractSessionCache
+ 1676 Remove Deprecated classes & methods
+ 1679 DeploymentManagerMBean not usable through JMX
+ 1682 Jetty-WarFragmentFolderPath directive has no effect in eclipse runtime
mode except for the first launch
+ 1692 Annotation scanning should ignore `module-info.class` files
+ 1698 Missing WWW-Authenticate from SpnegoAuthenticator when other
Authorization header provided
+ 1746 Remove LICENSE-CONTRIBUTOR?
+ 1836 Migrate Locker implementation to JVM ReentrantLock implementation
+ 1838 Servlet 4.0.0 artifact now available on central.maven.org
+ 1852 Fix quickstart generation for servlet 4.0
+ 1898 Request.getCookie() should ignore invalid cookies
+ 1956 Store and report build information of Jetty
+ 1977 jetty-http-spi tests fail with java9
+ 2061 WebSocket hangs in blockingWrite
+ 2075 Deprecating MultiException
+ 2095 Remove FastCGI multiplexing
+ 2103 Server should open connectors early in start sequence
+ 2108 Update licence headers and plugin for 2018
+ 2172 Support javax.websocket 1.1
+ 2175 Refactor WebSocket close handling
+ 2191 JPMS Support
+ 2431 Upgrade to Junit 5
+ 2868 Adding SPNEGO authentication support for Jetty Client
+ 2901 Introduce HttpConnectionUpgrader as a conversation component in
HttpClient
+ 2909 Remove B64Code
+ 2948 Require JDK 11 for Jetty 10.x
+ 2978 Add module-info.java to relevant Jetty modules
+ 2983 Jetty 10 Configuration abstraction
+ 2985 Jetty 10 Configuration replacement algorithm incorrect
+ 2996 ContextHandler.setDefaultContextPath() not implemented for quickstart.
+ 3009 Update Jetty 10 to use non-LEGACY Compliance Modes
+ 3010 Move old MultiPart parsing implementation to jetty-http
+ 3011 Move HttpCompliance to HttpConfiguration
+ 3012 Refactor HttpCompliance and HttpComplianceSection to be friendlier to
customization
+ 3106 Websocket connection stats and request stats
+ 3129 javax-websocket-common pom.xml is wrong
+ 3139 NPE on
WebSocketServerContainerInitializer.configureContext(ServletContextHandler)
+ 3154 Add support for javax.net.ssl.HostnameVerifier to HttpClient
+ 3159 WebSocket permessage-deflate RSV1 validity check
+ 3162 Use Jetty specific Servlet API jar
+ 3165 Review javax-websocket-server tests
+ 3166 Run of autobahn websocket tests on CI
+ 3167 JavaxWebSocketServerContainerInitializer always creates a HttpClient
+ 3170 WebSocket proxy PoC
+ 3182 Restore websocket example files
+ 3186 Jetty maven plugin - javax.annotation.jar picked up from jetty plugin
rather than from applications classpath
+ 3197 Use jetty specific websocket API jar
+ 3213 MetaInfConfigurationTest tests disabled in jetty-10.0.x
+ 3216 Autobahn WebSocketServer failures in jetty 10
+ 3225 Response.sendError should not set reason.
+ 3246 javax-websocket-tests exception stacktraces
+ 3249 Update to apache jasper 9.0.14 for jetty-10
+ 3274 OSGi versions of java.base classes in
org.apache.felix:org.osgi.foundation:jar conflicts with new rules on Java 9+
+ 3279 WebSocket write may hang forever
+ 3288 Correct websocket artifactIds on jetty-10.0.x
+ 3290 async websocket onOpen, onError and onClose in 10.0.x
+ 3298 Review jetty-10 websocket CompletableFuture usage.
+ 3303 Update to jakarta ee javax artifacts for jetty-10
+ 3308 Remove deprecated methods from sessions
+ 3320 Review Jetty 10 module-info.java
+ 3333 Jetty 10 standalone cannot start on the module-path
+ 3340 Update PushCacheFilter to use Servlet 4.0 APIs
+ 3341 XmlBasedHttpClientProvider in Jetty 10
+ 3351 Restructure jetty-unixsocket module
+ 3374 JSR356 RemoteEndpoint.Async.setSendTimeout() logic invalid in Jetty
10.0.x
+ 3379 Tracking of WebSocket Sessions in WebSocket containers
+ 3380 WebSocket should support jetty-io Connection.Listener
+ 3382 Jetty WebSocket Session.suspend() not implemented
+ 3399 XmlConfiguration jetty.webapps.uri is the uri of the webapp not the
parent dir
+ 3406 restore and fix jetty websocket tests in jetty 10
+ 3412 problems with jetty 10 WebSocket session customizer
+ 3446 allow jetty WebSockets to be upgraded using WebSocketUpgradeFilter in
jetty-10
+ 3453 Removing moved Extension classes from jetty-websocket-api
+ 3458 ensure users of the jetty-websocket-api do not have to see
websocket-core classes
+ 3462 client validation of websocket upgrade response
+ 3465 websocket negotiation of extension configuration parameters
+ 3479 review and cleanup of jetty-websocket-api in jetty-10
+ 3484 ClassCastException when using websocket-core classes in
websocket-servlet
+ 3494 flaky tests in jetty-websocket-tests ClientCloseTest
+ 3564 Update jetty-10.0.x to apache jsp 9.0.19
+ 3608 Reply with 400 Bad request to malformed WebSocket handshake
+ 3616 Backport WebSocket SessionTracker from Jetty 10
+ 3648 javax.websocket client container incorrectly creates Server
SslContextFactory
+ 3661 JettyWebSocketServerContainer exposes websocket common classes
+ 3666 WebSocket - Handling sent 1009 close frame.
+ 3696 Unwrap JavaxWebSocketClientContainer.connectToServer() exceptions
+ 3698 Missing WebSocket ServerContainer after server restart
+ 3700 stackoverflow in WebAppClassLoaderUrlStreamTest
+ 3705 Review ClientUpgradeRequest exception handling
+ 3708 Swap various java.lang.String replace() methods for better performant
ones
+ 3712 change maxIdleTime to idleTimeout in jetty-10 websockets
+ 3719 Clean up jetty-10 modules
+ 3726 Remove OSGi export uses of servlet-api from jetty-util
+ 3731 Add testing of CDI behaviors
+ 3736 NPE from WebAppClassLoader during CDI
+ 3746 ClassCastException in WriteFlusher.java - IdleState cannot be cast to
FailedState
+ 3749 Memory leak while processing AsyncListener annotations
+ 3751 Modern Configure DTD / FPI is used inconsistently
+ 3755 ServerWithAnnotations doesn't do anything
+ 3758 Avoid sending empty trailer frames for http/2 requests
+ 3762 WebSocketConnectionStatsTest test uses port 8080
+ 3782 X-Forwarded-Port overrides X-Forwarded-For
+ 3786 ALPN support for Java 14
+ 3789 XmlConfiguration set from property
+ 3798 ClasspathPattern match method throws NPE. URI can be null
+ 3799 Programmatically added listeners from
ServletContextListener.contextInitialzed() are not called
+ 3804 Weld/CDI XML backwards compat?
+ 3805 XmlConfiguration odd behavior for numbers
+ 3809 sending WebSocket close frame with error StatusCode does not do a hard
close (Jetty-10)
+ 3815 PropertyFileLoginModule adds user principle as a role
+ 3835 WebSocketSession are not being stopped properly
+ 3839 JavaxWebSocketServletContainerInitializer fails
+ 3840 Byte-range request performance problems with large files
+ 3849 ClosedChannelException from jetty-test-webapp javax websocket chat
example
jetty-9.4.20.v20190813 - 13 August 2019
+ 300 Implement Deflater / Inflater Object Pool
+ 2061 WebSocket hangs in blockingWrite
+ 3601 HTTP2 stall on reset streams
+ 3648 javax.websocket client container incorrectly creates Server
SslContextFactory
+ 3698 Missing WebSocket ServerContainer after server restart
+ 3700 stackoverflow in WebAppClassLoaderUrlStreamTest
+ 3708 Swap various java.lang.String replace() methods for better performant
ones
+ 3731 Add testing of CDI behaviors
+ 3736 NPE from WebAppClassLoader during CDI
+ 3746 ClassCastException in WriteFlusher.java - IdleState cannot be cast to
FailedState
+ 3749 Memory leak while processing AsyncListener annotations
+ 3755 ServerWithAnnotations doesn't do anything
+ 3758 Avoid sending empty trailer frames for http/2 requests
+ 3782 X-Forwarded-Port overrides X-Forwarded-For
+ 3786 ALPN support for Java 14
+ 3798 ClasspathPattern match method throws NPE. URI can be null
+ 3799 Programmatically added listeners from
ServletContextListener.contextInitialzed() are not called
+ 3804 Weld/CDI XML backwards compat
+ 3805 XmlConfiguration odd behavior for numbers
+ 3806 The error page handler didn't process correctly in proxy
+ 3815 PropertyFileLoginModule adds user principle as a role
+ 3822 trustAll will not work on some servers
+ 3829 Avoid sending empty trailer frames for http/2 responses
+ 3835 WebSocketSession are not being stopped properly
+ 3840 Byte-range request performance problems with large files
+ 3856 Different behaviour with maxFormContentSize=0 if Content-Length header
is present/missing
+ 3876 WebSocketPartialListener is only called for initial frames, not for
continuation frames
+ 3884 @WebSocket without @OnWebSocketMessage handler fails when receiving a
continuation frame
+ 3888 BufferUtil.toBuffer(Resource resource,boolean direct) does not like
large (4G+) Resources
+ 3906 Fix for #3840 breaks Path encapsulation in PathResource
+ 3929 Deadlock between new HTTP2Connection() and Server.stop()
+ 3940 Double initialization of Log
+ 3957 CustomRequestLog bad usage of MethodHandles.lookup()
+ 3960 Fix HttpConfiguration copy constructor
+ 3969 X-Forwarded-Port header customization isn't possible
jetty-9.4.19.v20190610 - 10 June 2019
+ 2909 Remove B64Code
+ 3332 jetty-maven-plugin - transitive dependencies not loaded from

View File

@ -347,7 +347,7 @@
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="128"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="512"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>

View File

@ -1,4 +1,4 @@
<code_scheme name="Jetty-StyleChecked" version="173">
<code_scheme name="Jetty" version="173">
<option name="AUTODETECT_INDENTS" value="false" />
<option name="LINE_SEPARATOR" value="&#xA;" />
<option name="RIGHT_MARGIN" value="128" />
@ -9,7 +9,6 @@
<option name="HTML_ATTRIBUTE_WRAP" value="0" />
</HTMLCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="FIELD_NAME_PREFIX" value="_" />
<option name="USE_EXTERNAL_ANNOTATIONS" value="true" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
@ -67,9 +66,11 @@
<option name="WHILE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="SPACE_AFTER_TYPE_CAST" value="false" />
<option name="RESOURCE_LIST_WRAP" value="2" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="false" />
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
@ -80,6 +81,7 @@
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</indentOptions>
<arrangement>
<rules>

View File

@ -1,9 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>build-resources</artifactId>
@ -17,10 +12,7 @@
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<!-- we would prefer not deploy it but we need as it's not discovered as part of reactor
as it's a dependency of a plugin
<skip>true</skip>
-->
</configuration>
</plugin>
</plugins>

View File

@ -33,22 +33,16 @@
</module>
<module name="TreeWalker">
<!--
Eclipse Jetty Specific.
===========================================================================================
-->
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="@checkstyle-disable-check : ([\w\|]+)"/>
<property name="onCommentFormat" value="@checkstyle-enable-check : ([\w\|]+)"/>
<property name="checkFormat" value="$1"/>
</module>
<!-- Check abbreviations(consecutive capital letters) length in identifier name -->
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="true"/>
<property name="allowedAbbreviations" value="ALPN, ASCII, AWT, CRLDP, CRLF, FCGI, GZIP, HTTP, HTTPS, ID, IP, ISO8859, JAAS, JDBC, JMXRMI, JNDI, JPMS, JSON, JSTL, LDAP, PROXY, RFC, SPNEGO, URI, URL, UTF8, XML"/>
</module>
<!--
Eclipse Jetty Specific.
===========================================================================================
-->
<!-- Location of Annotations -->
<module name="AnnotationLocation">
@ -90,7 +84,7 @@
<!-- Indentation Rules -->
<module name="Indentation">
<property name="throwsIndent" value="0"/>
<property name="arrayInitIndent" value="8"/>
</module>
<!-- Interface Type Parameter Name -->
@ -215,7 +209,9 @@
</module>
<!-- all switch statements should have "default" label declared -->
<!-- Disabled: Is super noisy
<module name="MissingSwitchDefault"/>
-->
<!-- prevent line wrapping of import / package statements -->
<module name="NoLineWrap"/>
@ -226,9 +222,6 @@
<!-- Filename and Classname match -->
<module name="OuterTypeFilename"/>
<!-- Checks that overload methods are grouped together -->
<module name="OverloadMethodsDeclarationOrder"/>
<!--
Checks based on the Java Language Specification recommendations.
https://docs.oracle.com/javase/specs/jls/se8/html/index.html
@ -284,8 +277,5 @@
<module name="UpperEll"/>
<!-- TODO: look for float / double version of above -->
<!-- Checks the distance between declaration of variable and its first usage -->
<module name="VariableDeclarationUsageDistance"/>
</module>
</module>

View File

@ -188,11 +188,12 @@ public class AnnotationConfiguration extends AbstractConfiguration
/**
* ServletContainerInitializerOrdering
*
* A list of classnames of ServletContainerInitializers in the order in which
* they are to be called back. One name only in the list can be "*", which is a
* <p>Applies an ordering to the {@link ServletContainerInitializer}s for the context, using
* the value of the "org.eclipse.jetty.containerInitializerOrder" context attribute.
* The attribute value is a list of classnames of ServletContainerInitializers in the order in which
* they are to be called. One name only in the list can be "*", which is a
* wildcard which matches any other ServletContainerInitializer name not already
* matched.
* matched.</p>
*/
public class ServletContainerInitializerOrdering
{

View File

@ -5,14 +5,34 @@
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.cdi</groupId>
<artifactId>cdi-2</artifactId>
<name>Jetty :: CDI 2</name>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-cdi</artifactId>
<name>Jetty :: CDI</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>jar</packaging>
<properties>
<bundle-symbolic-name>${project.groupId}.cdi2</bundle-symbolic-name>
<bundle-symbolic-name>${project.groupId}.cdi</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="setAttribute">
<Arg type="String">org.eclipse.jetty.cdi</Arg>
<Arg><Property name="jetty.cdi.mode" default="CdiSpiDecorator"/></Arg>
</Call>
</Configure>

View File

@ -0,0 +1,18 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Configures Jetty to use the "CdiDecoratingListener" as the default
CDI integration mode that allows a webapp to register it's own CDI
decorator.
[tag]
cdi
[provides]
cdi-mode
[depend]
cdi
[ini]
jetty.cdi.mode=CdiDecoratingListener

View File

@ -0,0 +1,17 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Configures Jetty to use the "CdiSpiDecorator" that calls the CDI SPI
as the default CDI integration mode.
[tag]
cdi
[provides]
cdi-mode
[depend]
cdi
[ini]
jetty.cdi.mode=CdiSpiDecorator

View File

@ -1,7 +1,34 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Jetty setup to support Weld/CDI2 with WELD inside the webapp
Support for CDI inside the webapp.
This module does not provide CDI, but configures jetty to support various
integration modes with a CDI implementation on the webapp classpath.
CDI integration modes can be selected per webapp with the "org.eclipse.jetty.cdi"
init parameter or defaults to the mode set by the "org.eclipse.jetty.cdi" server
attribute (which is initialised from the "jetty.cdi.mode" start property).
Supported modes are:
CdiSpiDecorator - Jetty will call the CDI SPI within the webapp to decorate
objects (default).
CdiDecoratingLister - The webapp may register a decorator on the context attribute
"org.eclipse.jetty.cdi.decorator".
[tag]
cdi
[provides]
cdi
[depend]
cdi2
deploy
[xml]
etc/cdi/jetty-cdi.xml
[lib]
lib/jetty-cdi-${jetty.version}.jar
lib/apache-jsp/org.mortbay.jasper.apache-el-*.jar
[ini]
jetty.webapp.addSystemClasses+=,org.eclipse.jetty.cdi.CdiServletContainerInitializer
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.cdi.CdiServletContainerInitializer

View File

@ -1,20 +0,0 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Jetty setup to support Weld/CDI2 with WELD inside the webapp
[depend]
annotations
[lib]
lib/apache-jsp/org.mortbay.jasper.apache-el-*.jar
[ini]
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.util.Decorator,-org.eclipse.jetty.util.DecoratedObjectFactory
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.server.handler.ContextHandler.,-org.eclipse.jetty.server.handler.ContextHandler,-org.eclipse.jetty.servlet.ServletContextHandler
[license]
Weld is an open source project hosted on Github and released under the Apache 2.0 license.
http://weld.cdi-spec.org/
http://www.apache.org/licenses/LICENSE-2.0.html

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// 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;
import org.eclipse.jetty.servlet.DecoratingListener;
import org.eclipse.jetty.servlet.ServletContextHandler;
/**
* A DecoratingListener that listens for "org.eclipse.jetty.cdi.decorator"
*/
class CdiDecoratingListener extends DecoratingListener
{
public static final String MODE = "CdiDecoratingListener";
public static final String ATTRIBUTE = "org.eclipse.jetty.cdi.decorator";
public CdiDecoratingListener(ServletContextHandler contextHandler)
{
super(contextHandler, ATTRIBUTE);
}
}

View File

@ -0,0 +1,99 @@
//
// ========================================================================
// 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;
import java.util.Objects;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* <p>A {@link ServletContainerInitializer} that introspects for a CDI API
* implementation within a web application and applies an integration
* mode if CDI is found. CDI integration modes can be selected per webapp with
* the "org.eclipse.jetty.cdi" init parameter or default to the mode set by the
* "org.eclipse.jetty.cdi" server attribute. Supported modes are:</p>
* <dl>
* <dt>CdiSpiDecorator</dt>
* <dd>Jetty will call the CDI SPI within the webapp to decorate objects (default).</dd>
* <dt>CdiDecoratingLister</dt>
* <dd>The webapp may register a decorator on the context attribute
* "org.eclipse.jetty.cdi.decorator".</dd>
* </dl>
*
* @see AnnotationConfiguration.ServletContainerInitializerOrdering
*/
public class CdiServletContainerInitializer implements ServletContainerInitializer
{
public static final String CDI_INTEGRATION_ATTRIBUTE = "org.eclipse.jetty.cdi";
private static final Logger LOG = Log.getLogger(CdiServletContainerInitializer.class);
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx)
{
try
{
ServletContextHandler context = ServletContextHandler.getServletContextHandler(ctx);
Objects.requireNonNull(context);
// Test if CDI is in the webapp by trying to load the CDI class.
ClassLoader loader = context.getClassLoader();
if (loader == null)
Loader.loadClass("javax.enterprise.inject.spi.CDI");
else
loader.loadClass("javax.enterprise.inject.spi.CDI");
String mode = ctx.getInitParameter(CDI_INTEGRATION_ATTRIBUTE);
if (mode == null)
{
mode = (String)context.getServer().getAttribute(CDI_INTEGRATION_ATTRIBUTE);
if (mode == null)
mode = CdiSpiDecorator.MODE;
}
switch (mode)
{
case CdiSpiDecorator.MODE:
context.getObjectFactory().addDecorator(new CdiSpiDecorator(context));
break;
case CdiDecoratingListener.MODE:
context.addEventListener(new CdiDecoratingListener(context));
break;
default:
throw new IllegalStateException(mode);
}
context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode);
LOG.info(mode + " enabled in " + ctx);
}
catch (UnsupportedOperationException | ClassNotFoundException e)
{
if (LOG.isDebugEnabled())
LOG.debug("CDI not found in " + ctx, e);
}
}
}

View File

@ -0,0 +1,166 @@
//
// ========================================================================
// 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;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.Decorator;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* A Decorator that invokes the CDI provider within a webapp to decorate objects created by
* the contexts {@link org.eclipse.jetty.util.DecoratedObjectFactory}
* (typically Listeners, Filters and Servlets).
* The CDI provider is invoked using {@link MethodHandle}s to avoid any CDI instance
* or dependencies within the server scope. The code invoked is equivalent to:
* <pre>
* public &lt;T&gt; T decorate(T o)
* {
* BeanManager manager = CDI.current().getBeanManager();
* manager.createInjectionTarget(manager.createAnnotatedType((Class&lt;T&gt;)o.getClass()))
* .inject(o,manager.createCreationalContext(null));
* return o;
* }
* </pre>
*/
public class CdiSpiDecorator implements Decorator
{
private static final Logger LOG = Log.getLogger(CdiServletContainerInitializer.class);
public static final String MODE = "CdiSpiDecorator";
private final ServletContextHandler _context;
private final Map<Object, Decorated> _decorated = new HashMap<>();
private final MethodHandle _current;
private final MethodHandle _getBeanManager;
private final MethodHandle _createAnnotatedType;
private final MethodHandle _createInjectionTarget;
private final MethodHandle _createCreationalContext;
private final MethodHandle _inject;
private final MethodHandle _dispose;
private final MethodHandle _release;
public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException
{
_context = context;
ClassLoader classLoader = _context.getClassLoader();
try
{
Class<?> cdiClass = classLoader.loadClass("javax.enterprise.inject.spi.CDI");
Class<?> beanManagerClass = classLoader.loadClass("javax.enterprise.inject.spi.BeanManager");
Class<?> annotatedTypeClass = classLoader.loadClass("javax.enterprise.inject.spi.AnnotatedType");
Class<?> injectionTargetClass = classLoader.loadClass("javax.enterprise.inject.spi.InjectionTarget");
Class<?> creationalContextClass = classLoader.loadClass("javax.enterprise.context.spi.CreationalContext");
Class<?> contextualClass = classLoader.loadClass("javax.enterprise.context.spi.Contextual");
MethodHandles.Lookup lookup = MethodHandles.lookup();
_current = lookup.findStatic(cdiClass, "current", MethodType.methodType(cdiClass));
_getBeanManager = lookup.findVirtual(cdiClass, "getBeanManager", MethodType.methodType(beanManagerClass));
_createAnnotatedType = lookup.findVirtual(beanManagerClass, "createAnnotatedType", MethodType.methodType(annotatedTypeClass, Class.class));
_createInjectionTarget = lookup.findVirtual(beanManagerClass, "createInjectionTarget", MethodType.methodType(injectionTargetClass, annotatedTypeClass));
_createCreationalContext = lookup.findVirtual(beanManagerClass, "createCreationalContext", MethodType.methodType(creationalContextClass, contextualClass));
_inject = lookup.findVirtual(injectionTargetClass, "inject", MethodType.methodType(Void.TYPE, Object.class, creationalContextClass));
_dispose = lookup.findVirtual(injectionTargetClass, "dispose", MethodType.methodType(Void.TYPE, Object.class));
_release = lookup.findVirtual(creationalContextClass, "release", MethodType.methodType(Void.TYPE));
}
catch (Exception e)
{
throw new UnsupportedOperationException(e);
}
}
/**
* Decorate an object.
* <p>The signature of this method must match what is introspected for by the
* Jetty DecoratingListener class. It is invoked dynamically.</p>
*
* @param o The object to be decorated
* @param <T> The type of the object to be decorated
* @return The decorated object
*/
public <T> T decorate(T o)
{
try
{
if (LOG.isDebugEnabled())
LOG.debug("decorate {} in {}", o, _context);
_decorated.put(o, new Decorated(o));
}
catch (Throwable th)
{
LOG.warn("Unable to decorate " + o, th);
}
return o;
}
/**
* Destroy a decorated object.
* <p>The signature of this method must match what is introspected for by the
* Jetty DecoratingListener class. It is invoked dynamically.</p>
*
* @param o The object to be destroyed
*/
public void destroy(Object o)
{
try
{
Decorated decorated = _decorated.remove(o);
if (decorated != null)
decorated.destroy(o);
}
catch (Throwable th)
{
LOG.warn("Unable to destroy " + o, th);
}
}
private class Decorated
{
private final Object _injectionTarget;
private final Object _creationalContext;
Decorated(Object o) throws Throwable
{
// BeanManager manager = CDI.current().getBeanManager();
Object manager = _getBeanManager.invoke(_current.invoke());
// AnnotatedType annotatedType = manager.createAnnotatedType((Class<T>)o.getClass());
Object annotatedType = _createAnnotatedType.invoke(manager, o.getClass());
// CreationalContext creationalContext = manager.createCreationalContext(null);
_creationalContext = _createCreationalContext.invoke(manager, null);
// InjectionTarget injectionTarget = manager.createInjectionTarget();
_injectionTarget = _createInjectionTarget.invoke(manager, annotatedType);
// injectionTarget.inject(o, creationalContext);
_inject.invoke(_injectionTarget, o, _creationalContext);
}
public void destroy(Object o) throws Throwable
{
_dispose.invoke(_injectionTarget, o);
_release.invoke(_creationalContext);
}
}
}

View File

@ -0,0 +1 @@
org.eclipse.jetty.cdi.CdiServletContainerInitializer

View File

@ -72,7 +72,7 @@ public abstract class AbstractConnectorHttpClientTransport extends AbstractHttpC
context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, destination.getClientConnectionFactory());
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<>(promise));
context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, promise);
connector.connect(address, context);
}
}

View File

@ -509,13 +509,26 @@ public class HttpClient extends ContainerLifeCycle
* @param port the destination port
* @return the destination
* @see #getDestinations()
* @deprecated use {@link #resolveDestination(Request)} instead
*/
@Deprecated
public Destination getDestination(String scheme, String host, int port)
{
Origin origin = createOrigin(scheme, host, port);
return resolveDestination(new HttpDestination.Key(origin, null));
}
public Destination resolveDestination(Request request)
{
Origin origin = createOrigin(request.getScheme(), request.getHost(), request.getPort());
HttpClientTransport transport = getTransport();
HttpDestination.Key destinationKey = transport.newDestinationKey((HttpRequest)request, origin);
HttpDestination destination = resolveDestination(destinationKey);
if (LOG.isDebugEnabled())
LOG.debug("Resolved {} for {}", destination, request);
return destination;
}
private Origin createOrigin(String scheme, String host, int port)
{
if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme) &&
@ -529,7 +542,7 @@ public class HttpClient extends ContainerLifeCycle
return new Origin(scheme, host, port);
}
private HttpDestination resolveDestination(HttpDestination.Key key)
HttpDestination resolveDestination(HttpDestination.Key key)
{
HttpDestination destination = destinations.get(key);
if (destination == null)
@ -568,16 +581,7 @@ public class HttpClient extends ContainerLifeCycle
protected void send(final HttpRequest request, List<Response.ResponseListener> listeners)
{
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);
HttpDestination destination = (HttpDestination)resolveDestination(request);
destination.send(request, listeners);
}

View File

@ -50,6 +50,15 @@ public interface HttpClientTransport extends ClientConnectionFactory
*/
public void setHttpClient(HttpClient client);
/**
* 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);
/**
* Creates a new, transport-specific, {@link HttpDestination} object.
* <p>
@ -78,20 +87,4 @@ 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

@ -20,14 +20,15 @@ package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.Objects;
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.http.HttpConnectionOverHTTP;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
@ -37,7 +38,6 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class HttpProxy extends ProxyConfiguration.Proxy
{
@ -50,7 +50,12 @@ public class HttpProxy extends ProxyConfiguration.Proxy
public HttpProxy(Origin.Address address, boolean secure)
{
super(address, secure);
this(address, secure, new HttpDestination.Protocol(List.of("http/1.1"), false));
}
public HttpProxy(Origin.Address address, boolean secure, HttpDestination.Protocol protocol)
{
super(address, secure, Objects.requireNonNull(protocol));
}
@Override
@ -61,9 +66,14 @@ public class HttpProxy extends ProxyConfiguration.Proxy
@Override
public URI getURI()
{
return URI.create(newOrigin().asString());
}
private Origin newOrigin()
{
String scheme = isSecure() ? HttpScheme.HTTPS.asString() : HttpScheme.HTTP.asString();
return URI.create(new Origin(scheme, getAddress()).asString());
return new Origin(scheme, getAddress());
}
private class HttpProxyClientConnectionFactory implements ClientConnectionFactory
@ -79,33 +89,26 @@ public class HttpProxy extends ProxyConfiguration.Proxy
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
if (destination.isSecure())
HttpDestination.Protocol serverProtocol = destination.getKey().getProtocol();
boolean sameProtocol = proxySpeaksServerProtocol(serverProtocol);
if (destination.isSecure() || !sameProtocol)
{
if (sslContextFactory != null)
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
Promise<Connection> wrapped = promise;
if (promise instanceof Promise.Wrapper)
wrapped = ((Promise.Wrapper<Connection>)promise).unwrap();
if (wrapped instanceof TunnelPromise)
{
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
Promise<Connection> wrapped = promise;
if (promise instanceof Promise.Wrapper)
wrapped = ((Promise.Wrapper<Connection>)promise).unwrap();
if (wrapped instanceof TunnelPromise)
{
((TunnelPromise)wrapped).setEndPoint(endPoint);
return connectionFactory.newConnection(endPoint, context);
}
else
{
// Replace the promise with the proxy promise that creates the tunnel to the server.
CreateTunnelPromise tunnelPromise = new CreateTunnelPromise(connectionFactory, endPoint, promise, context);
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, tunnelPromise);
return connectionFactory.newConnection(endPoint, context);
}
// In case the server closes the tunnel (e.g. proxy authentication
// required: 407 + Connection: close), we will open another tunnel
// so we need to tell the promise about the new EndPoint.
((TunnelPromise)wrapped).setEndPoint(endPoint);
return connectionFactory.newConnection(endPoint, context);
}
else
{
throw new IOException("Cannot tunnel request, missing " +
SslContextFactory.class.getName() + " in " + HttpClient.class.getName());
return newProxyConnection(endPoint, context);
}
}
else
@ -113,6 +116,34 @@ public class HttpProxy extends ProxyConfiguration.Proxy
return connectionFactory.newConnection(endPoint, context);
}
}
private boolean proxySpeaksServerProtocol(HttpDestination.Protocol serverProtocol)
{
return serverProtocol != null && getProtocol().getProtocols().stream().anyMatch(p -> serverProtocol.getProtocols().stream().anyMatch(p::equalsIgnoreCase));
}
private org.eclipse.jetty.io.Connection newProxyConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
// Replace the promise with the proxy promise that creates the tunnel to the server.
@SuppressWarnings("unchecked")
Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
CreateTunnelPromise tunnelPromise = new CreateTunnelPromise(connectionFactory, endPoint, promise, context);
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, tunnelPromise);
// Replace the destination with the proxy destination.
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
HttpDestination proxyDestination = client.resolveDestination(new HttpDestination.Key(newOrigin(), getProtocol()));
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, proxyDestination);
try
{
return connectionFactory.newConnection(endPoint, context);
}
finally
{
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
}
}
}
/**
@ -139,6 +170,8 @@ public class HttpProxy extends ProxyConfiguration.Proxy
@Override
public void succeeded(Connection connection)
{
// Replace the promise back with the original.
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
tunnel(destination, connection);
}
@ -154,61 +187,28 @@ public class HttpProxy extends ProxyConfiguration.Proxy
String target = destination.getOrigin().getAddress().asString();
Origin.Address proxyAddress = destination.getConnectAddress();
HttpClient httpClient = destination.getHttpClient();
long connectTimeout = httpClient.getConnectTimeout();
Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target)
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
.timeout(connectTimeout, TimeUnit.MILLISECONDS);
Request connect = new TunnelRequest(httpClient, proxyAddress)
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target);
ProxyConfiguration.Proxy proxy = destination.getProxy();
if (proxy != null && proxy.isSecure())
if (proxy.isSecure())
connect.scheme(HttpScheme.HTTPS.asString());
final HttpConversation conversation = ((HttpRequest)connect).getConversation();
conversation.setAttribute(EndPoint.class.getName(), endPoint);
connect.attribute(Connection.class.getName(), new ProxyConnection(destination, connection, promise));
connection.send(connect, result ->
{
// The EndPoint may have changed during the conversation, get the latest.
EndPoint endPoint = (EndPoint)conversation.getAttribute(EndPoint.class.getName());
if (result.isSucceeded())
{
Response response = result.getResponse();
if (response.getStatus() == HttpStatus.OK_200)
{
tunnelSucceeded(endPoint);
}
else
{
HttpResponseException failure = new HttpResponseException("Unexpected " + response +
" for " + result.getRequest(), response);
tunnelFailed(endPoint, failure);
}
}
else
{
tunnelFailed(endPoint, result.getFailure());
}
});
connection.send(connect, new TunnelListener(connect));
}
private void tunnelSucceeded(EndPoint endPoint)
{
try
{
// Replace the promise back with the original
context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory sslConnectionFactory = client.newSslClientConnectionFactory(connectionFactory);
HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection();
org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
// Creating the connection will link the new Connection the EndPoint,
// but we need the old Connection linked for the upgrade to do its job.
endPoint.setConnection(oldConnection);
ClientConnectionFactory connectionFactory = this.connectionFactory;
if (destination.isSecure())
connectionFactory = destination.newSslClientConnectionFactory(connectionFactory);
var oldConnection = endPoint.getConnection();
var newConnection = connectionFactory.newConnection(endPoint, context);
endPoint.upgrade(newConnection);
if (LOG.isDebugEnabled())
LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
@ -224,6 +224,40 @@ public class HttpProxy extends ProxyConfiguration.Proxy
endPoint.close();
promise.failed(failure);
}
private class TunnelListener extends Response.Listener.Adapter
{
private final HttpConversation conversation;
private TunnelListener(Request request)
{
this.conversation = ((HttpRequest)request).getConversation();
}
@Override
public void onHeaders(Response response)
{
// The EndPoint may have changed during the conversation, get the latest.
EndPoint endPoint = (EndPoint)conversation.getAttribute(EndPoint.class.getName());
if (response.getStatus() == HttpStatus.OK_200)
{
tunnelSucceeded(endPoint);
}
else
{
HttpResponseException failure = new HttpResponseException("Unexpected " + response +
" for " + response.getRequest(), response);
tunnelFailed(endPoint, failure);
}
}
@Override
public void onComplete(Result result)
{
if (result.isFailed())
tunnelFailed(endPoint, result.getFailure());
}
}
}
private class ProxyConnection implements Connection
@ -296,4 +330,12 @@ public class HttpProxy extends ProxyConfiguration.Proxy
conversation.setAttribute(EndPoint.class.getName(), endPoint);
}
}
public static class TunnelRequest extends HttpRequest
{
private TunnelRequest(HttpClient client, Origin.Address address)
{
super(client, new HttpConversation(), URI.create("http://" + address.asString()));
}
}
}

View File

@ -609,7 +609,7 @@ public abstract class HttpReceiver
}
@Override
protected Action process() throws Throwable
protected Action process()
{
while (true)
{

View File

@ -76,6 +76,7 @@ public class HttpRequest implements Request
private String query;
private String method = HttpMethod.GET.asString();
private HttpVersion version = HttpVersion.HTTP_1_1;
private boolean versionExplicit;
private long idleTimeout = -1;
private long timeout;
private long timeoutAt;
@ -215,10 +216,16 @@ public class HttpRequest implements Request
return version;
}
public boolean isVersionExplicit()
{
return versionExplicit;
}
@Override
public Request version(HttpVersion version)
{
this.version = Objects.requireNonNull(version);
this.versionExplicit = true;
return this;
}

View File

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

View File

@ -64,11 +64,13 @@ public class ProxyConfiguration
private final Set<String> excluded = new HashSet<>();
private final Origin.Address address;
private final boolean secure;
private final HttpDestination.Protocol protocol;
protected Proxy(Origin.Address address, boolean secure)
protected Proxy(Origin.Address address, boolean secure, HttpDestination.Protocol protocol)
{
this.address = address;
this.secure = secure;
this.protocol = protocol;
}
/**
@ -87,6 +89,11 @@ public class ProxyConfiguration
return secure;
}
public HttpDestination.Protocol getProtocol()
{
return protocol;
}
/**
* @return the list of origins that must be proxied
* @see #matches(Origin)

View File

@ -45,7 +45,7 @@ public class Socks4Proxy extends ProxyConfiguration.Proxy
public Socks4Proxy(Origin.Address address, boolean secure)
{
super(address, secure);
super(address, secure, null);
}
@Override
@ -64,7 +64,7 @@ public class Socks4Proxy extends ProxyConfiguration.Proxy
}
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
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();
@ -195,10 +195,9 @@ public class Socks4Proxy extends ProxyConfiguration.Proxy
try
{
HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
ClientConnectionFactory connectionFactory = this.connectionFactory;
if (destination.isSecure())
connectionFactory = client.newSslClientConnectionFactory(connectionFactory);
connectionFactory = destination.newSslClientConnectionFactory(connectionFactory);
org.eclipse.jetty.io.Connection newConnection = connectionFactory.newConnection(getEndPoint(), context);
getEndPoint().upgrade(newConnection);
if (LOG.isDebugEnabled())

View File

@ -29,7 +29,7 @@ import org.eclipse.jetty.util.Promise;
* {@link Destination} holds a pool of {@link Connection}s, but allows to create unpooled
* connections if the application wants full control over connection management via {@link #newConnection(Promise)}.
* <p>
* {@link Destination}s may be obtained via {@link HttpClient#getDestination(String, String, int)}
* {@link Destination}s may be obtained via {@link HttpClient#resolveDestination(Request)}
*/
public interface Destination
{

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.client.dynamic;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@ -78,7 +79,7 @@ import org.eclipse.jetty.io.EndPoint;
* 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
public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTransport
{
private final List<ClientConnectionFactory.Info> factoryInfos;
private final List<String> protocols;
@ -102,42 +103,48 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
super(connector);
addBean(connector);
if (factoryInfos.length == 0)
throw new IllegalArgumentException("Missing ClientConnectionFactory");
factoryInfos = new Info[]{HttpClientConnectionFactory.HTTP11};
this.factoryInfos = Arrays.asList(factoryInfos);
this.protocols = Arrays.stream(factoryInfos)
.flatMap(info -> info.getProtocols().stream())
.distinct()
.collect(Collectors.toList());
.flatMap(info -> info.getProtocols().stream())
.distinct()
.map(p -> p.toLowerCase(Locale.ENGLISH))
.collect(Collectors.toList());
for (ClientConnectionFactory.Info factoryInfo : factoryInfos)
{
addBean(factoryInfo);
}
setConnectionPoolFactory(destination ->
new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1));
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 http1 = "http/1.1";
String http2 = ssl ? "h2" : "h2c";
List<String> protocols = List.of();
if (request.getVersion() == HttpVersion.HTTP_2)
if (request.isVersionExplicit())
{
// The application is explicitly asking for HTTP/2, so exclude HTTP/1.1.
if (this.protocols.contains(http2))
protocols = List.of(http2);
HttpVersion version = request.getVersion();
String desired = version == HttpVersion.HTTP_2 ? http2 : http1;
if (this.protocols.contains(desired))
protocols = List.of(desired);
}
else
{
// Preserve the order of protocols chosen by the application.
// We need to keep multiple protocols in case the protocol
// is negotiated: e.g. [http/1.1, h2] negotiates [h2], but
// here we don't know yet what will be negotiated.
protocols = this.protocols.stream()
.filter(p -> p.equals("http/1.1") || p.equals(http2))
.filter(p -> p.equals(http1) || 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));
return new HttpDestination.Key(origin, new HttpDestination.Protocol(protocols, ssl && protocols.contains(http2)));
}
@Override
@ -166,7 +173,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
else
{
factoryInfo = findClientConnectionFactoryInfo(protocol.getProtocols())
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol));
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol));
}
}
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
@ -184,7 +191,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
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));
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for negotiated protocol " + protocol));
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
}
catch (Throwable failure)
@ -197,7 +204,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
private Optional<Info> findClientConnectionFactoryInfo(List<String> protocols)
{
return factoryInfos.stream()
.filter(info -> info.matches(protocols))
.findFirst();
.filter(info -> info.matches(protocols))
.findFirst();
}
}

View File

@ -19,12 +19,15 @@
package org.eclipse.jetty.client.http;
import java.io.IOException;
import java.util.List;
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.HttpRequest;
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;
@ -35,6 +38,8 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
@ManagedObject("The HTTP/1.1 client transport")
public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTransport
{
public static final HttpDestination.Protocol HTTP11 = new HttpDestination.Protocol(List.of("http/1.1"), false);
public HttpClientTransportOverHTTP()
{
this(Math.max(1, ProcessorUtils.availableProcessors() / 2));
@ -52,6 +57,12 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
setConnectionPoolFactory(destination -> new DuplexConnectionPool(destination, getHttpClient().getMaxConnectionsPerDestination(), destination));
}
@Override
public HttpDestination.Key newDestinationKey(HttpRequest request, Origin origin)
{
return new HttpDestination.Key(origin, HTTP11);
}
@Override
public HttpDestination newHttpDestination(HttpDestination.Key key)
{

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.client.http;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@ -28,6 +29,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.HttpProxy;
import org.eclipse.jetty.client.IConnection;
import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.client.api.Connection;
@ -256,6 +258,18 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements IConne
return send(channel, exchange);
}
@Override
protected void normalizeRequest(Request request)
{
super.normalizeRequest(request);
if (request instanceof HttpProxy.TunnelRequest)
{
long connectTimeout = getHttpClient().getConnectTimeout();
request.timeout(connectTimeout, TimeUnit.MILLISECONDS)
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS);
}
}
@Override
public void close()
{

View File

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

View File

@ -23,7 +23,6 @@ import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -55,7 +54,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
start(scenario, 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);
@ -85,22 +84,23 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
ContentResponse response = client.newRequest(host, port)
var request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.content(new StringContentProvider("0"))
.onRequestSuccess(request ->
.onRequestSuccess(r ->
{
HttpDestination destination = (HttpDestination)client.resolveDestination(r);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
assertFalse(connection.getEndPoint().isOutputShutdown());
})
.send();
});
ContentResponse response = request.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
assertArrayEquals(data, response.getContent());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
}
@ -111,7 +111,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
start(scenario, 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);
request.startAsync();
@ -122,27 +122,28 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
CountDownLatch resultLatch = new CountDownLatch(1);
long idleTimeout = 1000;
client.newRequest(host, port)
var request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
.onRequestSuccess(request ->
.onRequestSuccess(r ->
{
HttpDestination destination = (HttpDestination)client.resolveDestination(r);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
assertFalse(connection.getEndPoint().isOutputShutdown());
})
.send(result ->
{
if (result.isFailed())
resultLatch.countDown();
});
request.send(result ->
{
if (result.isFailed())
resultLatch.countDown();
});
assertTrue(resultLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
}
@ -154,7 +155,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
start(scenario, 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);
@ -183,30 +184,31 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.allocate(8));
CountDownLatch resultLatch = new CountDownLatch(1);
client.newRequest(host, port)
var request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.content(content)
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
.onRequestSuccess(request ->
.onRequestSuccess(r ->
{
HttpDestination destination = (HttpDestination)client.resolveDestination(r);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
assertFalse(connection.getEndPoint().isOutputShutdown());
})
.send(result ->
{
if (result.isFailed())
resultLatch.countDown();
});
request.send(result ->
{
if (result.isFailed())
resultLatch.countDown();
});
content.offer(ByteBuffer.allocate(8));
content.close();
assertTrue(resultLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
}
@ -217,7 +219,7 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
start(scenario, 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);
response.setContentLength(0);
@ -238,21 +240,22 @@ public class ClientConnectionCloseTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
ContentResponse response = client.newRequest(host, port)
var request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.onRequestSuccess(request ->
.onRequestSuccess(r ->
{
HttpDestination destination = (HttpDestination)client.resolveDestination(r);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
HttpConnectionOverHTTP connection = (HttpConnectionOverHTTP)connectionPool.getActiveConnections().iterator().next();
assertFalse(connection.getEndPoint().isOutputShutdown());
})
.onResponseHeaders(r -> r.getHeaders().remove(HttpHeader.CONNECTION))
.send();
.onResponseHeaders(r -> r.getHeaders().remove(HttpHeader.CONNECTION));
ContentResponse response = request.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
}
}

View File

@ -0,0 +1,70 @@
//
// ========================================================================
// 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;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class HttpClientCorrelationDataTest extends AbstractHttpClientServerTest
{
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testCorrelationData(Scenario scenario) throws Exception
{
String correlationName = "X-Correlation-Data";
String correlationData = "123456";
ThreadLocal<String> correlation = new ThreadLocal<>();
start(scenario, new EmptyServerHandler()
{
@Override
protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
assertEquals(correlationData, request.getHeader(correlationName));
}
});
client.getRequestListeners().add(new Request.Listener.Adapter()
{
@Override
public void onQueued(Request request)
{
request.header(correlationName, correlation.get());
}
});
correlation.set(correlationData);
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.timeout(5, TimeUnit.SECONDS)
.send();
assertEquals(200, response.getStatus());
}
}

View File

@ -116,7 +116,7 @@ public class HttpClientCustomProxyTest
{
private CAFEBABEProxy(Origin.Address address, boolean secure)
{
super(address, secure);
super(address, secure, null);
}
@Override

View File

@ -45,12 +45,12 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
{
start(scenario, new EmptyServerHandler());
Destination destination = client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
Destination destination = client.resolveDestination(request);
FuturePromise<Connection> futureConnection = new FuturePromise<>();
destination.newConnection(futureConnection);
try (Connection connection = futureConnection.get(5, TimeUnit.SECONDS))
{
Request request = client.newRequest(destination.getHost(), destination.getPort()).scheme(scenario.getScheme());
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);
ContentResponse response = listener.get(5, TimeUnit.SECONDS);
@ -71,11 +71,11 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
{
start(scenario, new EmptyServerHandler());
Destination destination = client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
Destination destination = client.resolveDestination(request);
FuturePromise<Connection> futureConnection = new FuturePromise<>();
destination.newConnection(futureConnection);
Connection connection = futureConnection.get(5, TimeUnit.SECONDS);
Request request = client.newRequest(destination.getHost(), destination.getPort()).scheme(scenario.getScheme());
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);
ContentResponse response = listener.get(5, TimeUnit.SECONDS);
@ -105,14 +105,14 @@ public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTe
{
start(scenario, new EmptyServerHandler());
Destination destination = client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
CountDownLatch responseLatch = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.onResponseSuccess(response -> responseLatch.countDown());
Destination destination = client.resolveDestination(request);
FuturePromise<Connection> futureConnection = new FuturePromise<>();
destination.newConnection(futureConnection);
Connection connection = futureConnection.get(5, TimeUnit.SECONDS);
CountDownLatch responseLatch = new CountDownLatch(1);
Request request = client.newRequest(destination.getHost(), destination.getPort())
.scheme(scenario.getScheme())
.onResponseSuccess(response -> responseLatch.countDown());
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);

View File

@ -120,10 +120,11 @@ public class HttpClientTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
String path = "/";
Response response = client.GET(scenario.getScheme() + "://" + host + ":" + port + path);
Request request = client.newRequest(scenario.getScheme() + "://" + host + ":" + port + path);
Response response = request.send();
assertEquals(200, response.getStatus());
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
long start = System.nanoTime();
@ -182,7 +183,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_GET_ResponseWithContent(Scenario scenario) throws Exception
{
final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
start(scenario, new AbstractHandler()
{
@Override
@ -206,8 +207,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_GET_WithParameters_ResponseWithContent(Scenario scenario) throws Exception
{
final String paramName1 = "a";
final String paramName2 = "b";
String paramName1 = "a";
String paramName2 = "b";
start(scenario, new AbstractHandler()
{
@Override
@ -239,8 +240,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_GET_WithParametersMultiValued_ResponseWithContent(Scenario scenario) throws Exception
{
final String paramName1 = "a";
final String paramName2 = "b";
String paramName1 = "a";
String paramName2 = "b";
start(scenario, new AbstractHandler()
{
@Override
@ -278,8 +279,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_POST_WithParameters(Scenario scenario) throws Exception
{
final String paramName = "a";
final String paramValue = "\u20AC";
String paramName = "a";
String paramValue = "\u20AC";
start(scenario, new AbstractHandler()
{
@Override
@ -310,9 +311,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_PUT_WithParameters(Scenario scenario) throws Exception
{
final String paramName = "a";
final String paramValue = "\u20AC";
final String encodedParamValue = URLEncoder.encode(paramValue, "UTF-8");
String paramName = "a";
String paramValue = "\u20AC";
String encodedParamValue = URLEncoder.encode(paramValue, "UTF-8");
start(scenario, new AbstractHandler()
{
@Override
@ -344,9 +345,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_POST_WithParameters_WithContent(Scenario scenario) throws Exception
{
final byte[] content = {0, 1, 2, 3};
final String paramName = "a";
final String paramValue = "\u20AC";
byte[] content = {0, 1, 2, 3};
String paramName = "a";
String paramValue = "\u20AC";
start(scenario, new AbstractHandler()
{
@Override
@ -389,7 +390,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final byte[] content = {0, 1, 2, 3};
byte[] content = {0, 1, 2, 3};
ContentResponse response = client.POST(scenario.getScheme() + "://localhost:" + connector.getLocalPort())
.onRequestContent((request, buffer) ->
{
@ -420,20 +421,18 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final AtomicInteger progress = new AtomicInteger();
AtomicInteger progress = new AtomicInteger();
ContentResponse response = client.POST(scenario.getScheme() + "://localhost:" + connector.getLocalPort())
.onRequestContent((request, buffer) ->
{
byte[] bytes = new byte[buffer.remaining()];
assertEquals(1, bytes.length);
buffer.get(bytes);
assertEquals(bytes[0], progress.getAndIncrement());
})
.content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{
2
}, new byte[]{3}, new byte[]{4}))
.timeout(5, TimeUnit.SECONDS)
.send();
.onRequestContent((request, buffer) ->
{
byte[] bytes = new byte[buffer.remaining()];
assertEquals(1, bytes.length);
buffer.get(bytes);
assertEquals(bytes[0], progress.getAndIncrement());
})
.content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
.timeout(5, TimeUnit.SECONDS)
.send();
assertNotNull(response);
assertEquals(200, response.getStatus());
@ -448,8 +447,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
client.setMaxConnectionsPerDestination(1);
final CountDownLatch latch = new CountDownLatch(1);
final CountDownLatch successLatch = new CountDownLatch(2);
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch successLatch = new CountDownLatch(2);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.onRequestBegin(request ->
@ -509,7 +508,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
try (StacklessLogging stackless = new StacklessLogging(org.eclipse.jetty.server.HttpChannel.class))
{
final CountDownLatch latch = new CountDownLatch(2);
CountDownLatch latch = new CountDownLatch(2);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.path("/one")
@ -568,10 +567,10 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
}
final CountDownLatch latch = new CountDownLatch(3);
final AtomicLong exchangeTime = new AtomicLong();
final AtomicLong requestTime = new AtomicLong();
final AtomicLong responseTime = new AtomicLong();
CountDownLatch latch = new CountDownLatch(3);
AtomicLong exchangeTime = new AtomicLong();
AtomicLong requestTime = new AtomicLong();
AtomicLong responseTime = new AtomicLong();
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.file(file)
@ -623,7 +622,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
// The second ByteBuffer set to null will throw an exception
@ -678,14 +677,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
start(scenario, new EmptyServerHandler());
final CountDownLatch latch = new CountDownLatch(1);
final String host = "localhost";
final int port = connector.getLocalPort();
CountDownLatch latch = new CountDownLatch(1);
String host = "localhost";
int port = connector.getLocalPort();
client.newRequest(host, port)
.scheme(scenario.getScheme())
.onRequestBegin(request ->
.onRequestBegin(r ->
{
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
HttpDestination destination = (HttpDestination)client.resolveDestination(r);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
connectionPool.getActiveConnections().iterator().next().close();
})
@ -706,7 +705,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
public void test_Request_IdleTimeout(Scenario scenario) throws Exception
{
final long idleTimeout = 1000;
long idleTimeout = 1000;
start(scenario, new AbstractHandler()
{
@Override
@ -724,8 +723,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final String host = "localhost";
final int port = connector.getLocalPort();
String host = "localhost";
int port = connector.getLocalPort();
assertThrows(TimeoutException.class, () ->
client.newRequest(host, port)
.scheme(scenario.getScheme())
@ -763,7 +762,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testHeaderProcessing(Scenario scenario) throws Exception
{
final String headerName = "X-Header-Test";
String headerName = "X-Header-Test";
start(scenario, new AbstractHandler()
{
@Override
@ -792,7 +791,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new EmptyServerHandler());
int count = 10;
final CountDownLatch latch = new CountDownLatch(count);
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; ++i)
{
client.newRequest("localhost", connector.getLocalPort())
@ -821,7 +820,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_HEAD_With_ResponseContentLength(Scenario scenario) throws Exception
{
final int length = 1024;
int length = 1024;
start(scenario, new AbstractHandler()
{
@Override
@ -870,7 +869,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
start(scenario, new EmptyServerHandler());
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
client.newRequest(host, port)
.send(result ->
{
@ -926,7 +925,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testCustomUserAgent(Scenario scenario) throws Exception
{
final String userAgent = "Test/1.0";
String userAgent = "Test/1.0";
start(scenario, new AbstractHandler()
{
@Override
@ -1009,7 +1008,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
start(scenario, new EmptyServerHandler());
final AtomicInteger counter = new AtomicInteger();
AtomicInteger counter = new AtomicInteger();
Request.Listener listener = new Request.Listener()
{
@Override
@ -1081,8 +1080,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
start(scenario, new EmptyServerHandler());
final AtomicInteger counter = new AtomicInteger();
final CountDownLatch latch = new CountDownLatch(1);
AtomicInteger counter = new AtomicInteger();
CountDownLatch latch = new CountDownLatch(1);
Response.Listener listener = new Response.Listener()
{
@Override
@ -1161,7 +1160,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void setOnCompleteCallbackWithBlockingSend(Scenario scenario) throws Exception
{
final byte[] content = new byte[512];
byte[] content = new byte[512];
new Random().nextBytes(content);
start(scenario, new AbstractHandler()
{
@ -1173,7 +1172,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final Exchanger<Response> ex = new Exchanger<>();
Exchanger<Response> ex = new Exchanger<>();
BufferingResponseListener listener = new BufferingResponseListener()
{
@Override
@ -1204,7 +1203,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testCustomHostHeader(Scenario scenario) throws Exception
{
final String host = "localhost";
String host = "localhost";
start(scenario, new AbstractHandler()
{
@Override
@ -1266,18 +1265,17 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
long timeout = 5000;
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.version(HttpVersion.HTTP_1_0)
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
.timeout(timeout, TimeUnit.MILLISECONDS);
FuturePromise<Connection> promise = new FuturePromise<>();
Destination destination = client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
Destination destination = client.resolveDestination(request);
destination.newConnection(promise);
try (Connection connection = promise.get(5, TimeUnit.SECONDS))
{
long timeout = 5000;
Request request = client.newRequest(destination.getHost(), destination.getPort())
.scheme(destination.getScheme())
.version(HttpVersion.HTTP_1_0)
.header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())
.timeout(timeout, TimeUnit.MILLISECONDS);
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);
ContentResponse response = listener.get(2 * timeout, TimeUnit.MILLISECONDS);
@ -1312,7 +1310,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testLongPollIsAbortedWhenClientIsStopped(Scenario scenario) throws Exception
{
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
start(scenario, new AbstractHandler()
{
@Override
@ -1324,7 +1322,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final CountDownLatch completeLatch = new CountDownLatch(1);
CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.send(result ->
@ -1343,7 +1341,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario) throws Exception
public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario)
{
Assumptions.assumeTrue(HttpScheme.HTTP.is(scenario.getScheme()));
@ -1356,7 +1354,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ParameterizedTest
@ArgumentsSource(NonSslScenarioProvider.class)
public void testBigContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario) throws Exception
public void testBigContentDelimitedByEOFWithSlowRequestHTTP10(Scenario scenario)
{
ExecutionException e = assertThrows(ExecutionException.class, () ->
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_0, 128 * 1024));
@ -1379,7 +1377,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
testContentDelimitedByEOFWithSlowRequest(scenario, HttpVersion.HTTP_1_1, 128 * 1024);
}
private void testContentDelimitedByEOFWithSlowRequest(final Scenario scenario, final HttpVersion version, int length) throws Exception
private void testContentDelimitedByEOFWithSlowRequest(Scenario scenario, HttpVersion version, int length) throws Exception
{
// This test is crafted in a way that the response completes before the request is fully written.
// With SSL, the response coming down will close the SSLEngine so it would not be possible to
@ -1388,7 +1386,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
// This is a limit of Java's SSL implementation that does not allow half closes.
Assumptions.assumeTrue(HttpScheme.HTTP.is(scenario.getScheme()));
final byte[] data = new byte[length];
byte[] data = new byte[length];
new Random().nextBytes(data);
start(scenario, new AbstractHandler()
{
@ -1424,8 +1422,8 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testRequestRetries(Scenario scenario) throws Exception
{
final int maxRetries = 3;
final AtomicInteger requests = new AtomicInteger();
int maxRetries = 3;
AtomicInteger requests = new AtomicInteger();
start(scenario, new AbstractHandler()
{
@Override
@ -1438,7 +1436,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
new RetryListener(client, scenario.getScheme(), "localhost", connector.getLocalPort(), maxRetries)
{
@Override
@ -1466,9 +1464,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final AtomicReference<Callback> callbackRef = new AtomicReference<>();
final CountDownLatch contentLatch = new CountDownLatch(1);
final CountDownLatch completeLatch = new CountDownLatch(1);
AtomicReference<Callback> callbackRef = new AtomicReference<>();
CountDownLatch contentLatch = new CountDownLatch(1);
CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.send(new Response.Listener.Adapter()
@ -1514,7 +1512,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
final AtomicBoolean open = new AtomicBoolean();
AtomicBoolean open = new AtomicBoolean();
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSslContextFactory(scenario.newClientSslContextFactory());
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)
@ -1535,7 +1533,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
});
client.start();
final CountDownLatch latch = new CountDownLatch(2);
CountDownLatch latch = new CountDownLatch(2);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.onRequestBegin(request ->
@ -1567,7 +1565,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
.method(HttpMethod.CONNECT)
.version(HttpVersion.HTTP_1_0);
FuturePromise<Connection> promise = new FuturePromise<>();
client.getDestination("http", host, port).newConnection(promise);
client.resolveDestination(request).newConnection(promise);
Connection connection = promise.get(5, TimeUnit.SECONDS);
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);

View File

@ -45,7 +45,7 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientUploadDuringServerShutdown
public class HttpClientUploadDuringServerShutdownTest
{
/**
* A server used in conjunction with {@link ClientSide}.
@ -117,8 +117,8 @@ public class HttpClientUploadDuringServerShutdown
{
int length = 16 * 1024 * 1024 + random.nextInt(16 * 1024 * 1024);
client.newRequest("localhost", 8888)
.content(new BytesContentProvider(new byte[length]))
.send(result -> latch.countDown());
.content(new BytesContentProvider(new byte[length]))
.send(result -> latch.countDown());
long sleep = 1 + random.nextInt(10);
TimeUnit.MILLISECONDS.sleep(sleep);
}
@ -229,10 +229,10 @@ public class HttpClientUploadDuringServerShutdown
// is being closed is used to send the request.
assertTrue(sendLatch.await(5, TimeUnit.SECONDS));
final CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
CountDownLatch completeLatch = new CountDownLatch(1);
var request = client.newRequest("localhost", connector.getLocalPort())
.timeout(10, TimeUnit.SECONDS)
.onRequestBegin(request ->
.onRequestBegin(r ->
{
try
{
@ -243,12 +243,12 @@ public class HttpClientUploadDuringServerShutdown
{
x.printStackTrace();
}
})
.send(result -> completeLatch.countDown());
});
request.send(result -> completeLatch.countDown());
assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
HttpDestination destination = (HttpDestination)client.getDestination("http", "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool pool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, pool.getConnectionCount());
assertEquals(0, pool.getIdleConnections().size());

View File

@ -18,14 +18,12 @@
package org.eclipse.jetty.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -67,20 +65,19 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
CountDownLatch headersLatch = new CountDownLatch(1);
CountDownLatch successLatch = new CountDownLatch(3);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
final CountDownLatch headersLatch = new CountDownLatch(1);
final CountDownLatch successLatch = new CountDownLatch(3);
client.newRequest(host, port)
.scheme(scenario.getScheme())
.onRequestSuccess(request -> successLatch.countDown())
request.onRequestSuccess(r -> successLatch.countDown())
.onResponseHeaders(response ->
{
assertEquals(0, idleConnections.size());
@ -118,18 +115,19 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
CountDownLatch beginLatch = new CountDownLatch(1);
CountDownLatch failureLatch = new CountDownLatch(2);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
final CountDownLatch beginLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(2);
client.newRequest(host, port).scheme(scenario.getScheme()).listener(new Request.Listener.Adapter()
request.listener(new Request.Listener.Adapter()
{
@Override
public void onBegin(Request request)
@ -143,7 +141,8 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
{
failureLatch.countDown();
}
}).send(new Response.Listener.Adapter()
})
.send(new Response.Listener.Adapter()
{
@Override
public void onComplete(Result result)
@ -170,51 +169,50 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
CountDownLatch successLatch = new CountDownLatch(3);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Queue<Connection> idleConnections = connectionPool.getIdleConnections();
Queue<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
final CountDownLatch successLatch = new CountDownLatch(3);
client.newRequest(host, port)
.scheme(scenario.getScheme())
.listener(new Request.Listener.Adapter()
request.listener(new Request.Listener.Adapter()
{
@Override
public void onBegin(Request request)
{
@Override
public void onBegin(Request request)
{
// Remove the host header, this will make the request invalid
request.header(HttpHeader.HOST, null);
}
// Remove the host header, this will make the request invalid
request.header(HttpHeader.HOST, null);
}
@Override
public void onSuccess(Request request)
{
successLatch.countDown();
}
})
.send(new Response.Listener.Adapter()
@Override
public void onSuccess(Request request)
{
@Override
public void onSuccess(Response response)
{
assertEquals(400, response.getStatus());
// 400 response also come with a Connection: close,
// so the connection is closed and removed
successLatch.countDown();
}
successLatch.countDown();
}
})
.send(new Response.Listener.Adapter()
{
@Override
public void onSuccess(Response response)
{
assertEquals(400, response.getStatus());
// 400 response also come with a Connection: close,
// so the connection is closed and removed
successLatch.countDown();
}
@Override
public void onComplete(Result result)
{
assertFalse(result.isFailed());
successLatch.countDown();
}
});
@Override
public void onComplete(Result result)
{
assertFalse(result.isFailed());
successLatch.countDown();
}
});
assertTrue(successLatch.await(30, TimeUnit.SECONDS));
@ -232,65 +230,64 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
CountDownLatch successLatch = new CountDownLatch(3);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
final long delay = 1000;
final CountDownLatch successLatch = new CountDownLatch(3);
client.newRequest(host, port)
.scheme(scenario.getScheme())
.listener(new Request.Listener.Adapter()
long delay = 1000;
request.listener(new Request.Listener.Adapter()
{
@Override
public void onBegin(Request request)
{
@Override
public void onBegin(Request request)
{
// Remove the host header, this will make the request invalid
request.header(HttpHeader.HOST, null);
}
// Remove the host header, this will make the request invalid
request.header(HttpHeader.HOST, null);
}
@Override
public void onHeaders(Request request)
{
try
{
TimeUnit.MILLISECONDS.sleep(delay);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
@Override
public void onSuccess(Request request)
{
successLatch.countDown();
}
})
.send(new Response.Listener.Adapter()
@Override
public void onHeaders(Request request)
{
@Override
public void onSuccess(Response response)
try
{
assertEquals(400, response.getStatus());
// 400 response also come with a Connection: close,
// so the connection is closed and removed
successLatch.countDown();
TimeUnit.MILLISECONDS.sleep(delay);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
@Override
public void onComplete(Result result)
{
assertFalse(result.isFailed());
successLatch.countDown();
}
});
@Override
public void onSuccess(Request request)
{
successLatch.countDown();
}
})
.send(new Response.Listener.Adapter()
{
@Override
public void onSuccess(Response response)
{
assertEquals(400, response.getStatus());
// 400 response also come with a Connection: close,
// so the connection is closed and removed
successLatch.countDown();
}
@Override
public void onComplete(Result result)
{
assertFalse(result.isFailed());
successLatch.countDown();
}
});
assertTrue(successLatch.await(delay * 30, TimeUnit.MILLISECONDS));
@ -306,21 +303,20 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
server.stop();
final CountDownLatch failureLatch = new CountDownLatch(2);
client.newRequest(host, port)
.scheme(scenario.getScheme())
.onRequestFailure((request, failure) -> failureLatch.countDown())
CountDownLatch failureLatch = new CountDownLatch(2);
request.onRequestFailure((r, x) -> failureLatch.countDown())
.send(result ->
{
assertTrue(result.isFailed());
@ -340,7 +336,7 @@ public class HttpConnectionLifecycleTest 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)
{
response.setHeader("Connection", "close");
baseRequest.setHandled(true);
@ -349,29 +345,28 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest(host, port)
.scheme(scenario.getScheme())
.send(new Response.Listener.Adapter()
CountDownLatch latch = new CountDownLatch(1);
request.send(new Response.Listener.Adapter()
{
@Override
public void onComplete(Result result)
{
@Override
public void onComplete(Result result)
{
assertFalse(result.isFailed());
assertEquals(0, idleConnections.size());
assertEquals(0, activeConnections.size());
latch.countDown();
}
});
assertFalse(result.isFailed());
assertEquals(0, idleConnections.size());
assertEquals(0, activeConnections.size());
latch.countDown();
}
});
assertTrue(latch.await(30, TimeUnit.SECONDS));
@ -388,7 +383,7 @@ public class HttpConnectionLifecycleTest 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)
{
response.setHeader("Connection", "close");
baseRequest.setHandled(true);
@ -398,23 +393,22 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,...");
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024 * 1024);
Arrays.fill(buffer.array(), (byte)'x');
client.newRequest(host, port)
.scheme(scenario.getScheme())
.content(new ByteBufferContentProvider(buffer))
request.content(new ByteBufferContentProvider(buffer))
.send(new Response.Listener.Adapter()
{
@Override
@ -446,19 +440,17 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
ContentResponse response = client.newRequest(host, port)
.scheme(scenario.getScheme())
.timeout(30, TimeUnit.SECONDS)
.send();
ContentResponse response = request.timeout(30, TimeUnit.SECONDS).send();
assertEquals(200, response.getStatus());
@ -479,18 +471,18 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), host, port);
Request request = client.newRequest(host, port).scheme(scenario.getScheme());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
final Collection<Connection> idleConnections = connectionPool.getIdleConnections();
Collection<Connection> idleConnections = connectionPool.getIdleConnections();
assertEquals(0, idleConnections.size());
final Collection<Connection> activeConnections = connectionPool.getActiveConnections();
Collection<Connection> activeConnections = connectionPool.getActiveConnections();
assertEquals(0, activeConnections.size());
client.setStrictEventOrdering(false);
ContentResponse response = client.newRequest(host, port)
.scheme(scenario.getScheme())
ContentResponse response = request
.onResponseBegin(response1 ->
{
// Simulate a HTTP 1.0 response has been received.

View File

@ -55,18 +55,17 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
Exception failure = new Exception("oops");
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.timeout(5, TimeUnit.SECONDS);
request.timeout(5, TimeUnit.SECONDS);
request.abort(failure);
request.send();
});
assertSame(failure, x.getCause());
// Make sure the pool is in a sane state.
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(1, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -79,32 +78,29 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
start(scenario, new EmptyServerHandler());
final Throwable cause = new Exception();
final AtomicBoolean aborted = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean begin = new AtomicBoolean();
Throwable cause = new Exception();
AtomicBoolean aborted = new AtomicBoolean();
CountDownLatch latch = new CountDownLatch(1);
AtomicBoolean begin = new AtomicBoolean();
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.listener(new Request.Listener.Adapter()
request.listener(new Request.Listener.Adapter()
{
@Override
public void onQueued(Request request)
{
@Override
public void onQueued(Request request)
{
aborted.set(request.abort(cause));
latch.countDown();
}
aborted.set(request.abort(cause));
latch.countDown();
}
@Override
public void onBegin(Request request)
{
begin.set(true);
}
})
.timeout(5, TimeUnit.SECONDS)
.send();
@Override
public void onBegin(Request request)
{
begin.set(true);
}
}).timeout(5, TimeUnit.SECONDS).send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
@ -112,7 +108,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause());
assertFalse(begin.get());
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -130,34 +126,31 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
final CountDownLatch committed = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.listener(new Request.Listener.Adapter()
request.listener(new Request.Listener.Adapter()
{
@Override
public void onBegin(Request request)
{
@Override
public void onBegin(Request request)
{
aborted.set(request.abort(cause));
latch.countDown();
}
aborted.set(request.abort(cause));
latch.countDown();
}
@Override
public void onCommit(Request request)
{
committed.countDown();
}
})
.timeout(5, TimeUnit.SECONDS)
.send();
@Override
public void onCommit(Request request)
{
committed.countDown();
}
}).timeout(5, TimeUnit.SECONDS).send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
if (aborted.get())
assertSame(cause, x.getCause());
assertFalse(committed.await(1, TimeUnit.SECONDS));
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -175,34 +168,31 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
final CountDownLatch committed = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.listener(new Request.Listener.Adapter()
request.listener(new Request.Listener.Adapter()
{
@Override
public void onHeaders(Request request)
{
@Override
public void onHeaders(Request request)
{
aborted.set(request.abort(cause));
latch.countDown();
}
aborted.set(request.abort(cause));
latch.countDown();
}
@Override
public void onCommit(Request request)
{
committed.countDown();
}
})
.timeout(5, TimeUnit.SECONDS)
.send();
@Override
public void onCommit(Request request)
{
committed.countDown();
}
}).timeout(5, TimeUnit.SECONDS).send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
if (aborted.get())
assertSame(cause, x.getCause());
assertFalse(committed.await(1, TimeUnit.SECONDS));
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -219,26 +209,24 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
// A) the request is failed before the response arrived
// B) the request is failed after the response arrived
final Throwable cause = new Exception();
final AtomicBoolean aborted = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
Throwable cause = new Exception();
AtomicBoolean aborted = new AtomicBoolean();
CountDownLatch latch = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.onRequestCommit(request ->
{
aborted.set(request.abort(cause));
latch.countDown();
})
.timeout(5, TimeUnit.SECONDS)
.send();
request.onRequestCommit(r ->
{
aborted.set(r.abort(cause));
latch.countDown();
}).timeout(5, TimeUnit.SECONDS).send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
if (aborted.get())
assertSame(cause, x.getCause());
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -249,11 +237,11 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testAbortOnCommitWithContent(Scenario scenario) throws Exception
{
final AtomicReference<IOException> failure = new AtomicReference<>();
AtomicReference<IOException> failure = new AtomicReference<>();
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
{
try
{
@ -269,35 +257,31 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
});
final Throwable cause = new Exception();
final AtomicBoolean aborted = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
Throwable cause = new Exception();
AtomicBoolean aborted = new AtomicBoolean();
CountDownLatch latch = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.onRequestCommit(request ->
request.onRequestCommit(r ->
{
aborted.set(r.abort(cause));
latch.countDown();
}).content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@Override
public long getLength()
{
aborted.set(request.abort(cause));
latch.countDown();
})
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@Override
public long getLength()
{
return -1;
}
})
.timeout(5, TimeUnit.SECONDS)
.send();
return -1;
}
}).timeout(5, TimeUnit.SECONDS).send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
if (aborted.get())
assertSame(cause, x.getCause());
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -314,7 +298,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
start(scenario, 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
{
try
{
@ -328,28 +312,25 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
});
final Throwable cause = new Exception();
final AtomicBoolean aborted = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
Throwable cause = new Exception();
AtomicBoolean aborted = new AtomicBoolean();
CountDownLatch latch = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scenario.getScheme());
ExecutionException x = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.onRequestContent((request, content) ->
request.onRequestContent((r, c) ->
{
aborted.set(r.abort(cause));
latch.countDown();
}).content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@Override
public long getLength()
{
aborted.set(request.abort(cause));
latch.countDown();
})
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
{
@Override
public long getLength()
{
return -1;
}
})
.timeout(5, TimeUnit.SECONDS)
.send();
return -1;
}
}).timeout(5, TimeUnit.SECONDS).send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
if (aborted.get())
@ -357,7 +338,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -369,11 +350,11 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void testInterrupt(Scenario scenario) throws Exception
{
final long delay = 1000;
long delay = 1000;
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
{
@ -389,10 +370,10 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.timeout(3 * delay, TimeUnit.MILLISECONDS)
.scheme(scenario.getScheme());
.timeout(3 * delay, TimeUnit.MILLISECONDS)
.scheme(scenario.getScheme());
final Thread thread = Thread.currentThread();
Thread thread = Thread.currentThread();
new Thread(() ->
{
try
@ -406,7 +387,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
}).start();
assertThrows(InterruptedException.class, () -> request.send());
assertThrows(InterruptedException.class, request::send);
}
@ParameterizedTest
@ -417,7 +398,7 @@ public class HttpRequestAbortTest 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
{
@ -432,13 +413,13 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
});
final Request request = client.newRequest("localhost", connector.getLocalPort())
.timeout(3 * delay, TimeUnit.MILLISECONDS)
.scheme(scenario.getScheme());
Request request = client.newRequest("localhost", connector.getLocalPort())
.timeout(3 * delay, TimeUnit.MILLISECONDS)
.scheme(scenario.getScheme());
final Throwable cause = new Exception();
final AtomicBoolean aborted = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
Throwable cause = new Exception();
AtomicBoolean aborted = new AtomicBoolean();
CountDownLatch latch = new CountDownLatch(1);
new Thread(() ->
{
try
@ -464,7 +445,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
assertSame(cause, x.getCause());
}
HttpDestination destination = (HttpDestination)client.getDestination(scenario.getScheme(), "localhost", connector.getLocalPort());
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getActiveConnections().size());
@ -479,7 +460,7 @@ public class HttpRequestAbortTest 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
{
@ -497,8 +478,8 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
final Throwable cause = new Exception();
final CountDownLatch latch = new CountDownLatch(1);
Request request = client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.timeout(3 * delay, TimeUnit.MILLISECONDS);
.scheme(scenario.getScheme())
.timeout(3 * delay, TimeUnit.MILLISECONDS);
request.send(result ->
{
assertTrue(result.isFailed());
@ -520,7 +501,7 @@ public class HttpRequestAbortTest 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);
if (!"/done".equals(request.getRequestURI()))
@ -555,10 +536,10 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
ExecutionException e = assertThrows(ExecutionException.class, () ->
{
client.newRequest("localhost", connector.getLocalPort())
.scheme(scenario.getScheme())
.path("/redirect")
.timeout(5, TimeUnit.SECONDS)
.send();
.scheme(scenario.getScheme())
.path("/redirect")
.timeout(5, TimeUnit.SECONDS)
.send();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
if (aborted.get())

View File

@ -147,7 +147,7 @@ public class ServerConnectionCloseTest
Thread.sleep(1000);
// Connection should have been removed from pool.
HttpDestination destination = (HttpDestination)client.getDestination("http", "localhost", port);
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount());

View File

@ -172,7 +172,7 @@ public class TLSServerConnectionCloseTest
Thread.sleep(1000);
// Connection should have been removed from pool.
HttpDestination destination = (HttpDestination)client.getDestination("http", "localhost", port);
HttpDestination destination = (HttpDestination)client.resolveDestination(request);
DuplexConnectionPool connectionPool = (DuplexConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount());

View File

@ -153,13 +153,13 @@ public class Usage
HttpClient client = new HttpClient();
client.start();
Request request = client.newRequest("localhost", 8080);
// Create an explicit connection, and use try-with-resources to manage it
FuturePromise<Connection> futureConnection = new FuturePromise<>();
client.getDestination("http", "localhost", 8080).newConnection(futureConnection);
client.resolveDestination(request).newConnection(futureConnection);
try (Connection connection = futureConnection.get(5, TimeUnit.SECONDS))
{
Request request = client.newRequest("localhost", 8080);
// Asynchronous send but using FutureResponseListener
FutureResponseListener listener = new FutureResponseListener(request);
connection.send(request, listener);
@ -293,15 +293,8 @@ public class Usage
try (OutputStream output = content.getOutputStream())
{
client.newRequest("localhost", 8080)
.content(content)
.send(new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
assertEquals(200, result.getResponse().getStatus());
}
});
.content(content)
.send(result -> assertEquals(200, result.getResponse().getStatus()));
output.write(new byte[1024]);
output.write(new byte[512]);

View File

@ -260,28 +260,27 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
String host = "localhost";
int port = connector.getLocalPort();
Destination destinationBefore = client.getDestination(scenario.getScheme(), host, port);
ContentResponse response = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.send();
Request request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
Destination destinationBefore = client.resolveDestination(request);
ContentResponse response = request.send();
assertEquals(200, response.getStatus());
Destination destinationAfter = client.getDestination(scenario.getScheme(), host, port);
Destination destinationAfter = client.resolveDestination(request);
assertSame(destinationBefore, destinationAfter);
client.setRemoveIdleDestinations(true);
response = client.newRequest(host, port)
request = client.newRequest(host, port)
.scheme(scenario.getScheme())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.send();
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
response = request.send();
assertEquals(200, response.getStatus());
destinationAfter = client.getDestination(scenario.getScheme(), host, port);
destinationAfter = client.resolveDestination(request);
assertNotSame(destinationBefore, destinationAfter);
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- Bind the jetty-web-decorate.xml to every deployed webapp -->
<Ref refid="DeploymentManager">
<Call name="addLifeCycleBinding">
<Arg>
<New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding">
<Set name="jettyXml"><Property name="jetty.home" default="." />/etc/jetty-web-decorate.xml
</Set>
</New>
</Arg>
</Call>
</Ref>
</Configure>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext" id="context">
<Get class="org.eclipse.jetty.webapp.DecoratingListener" name="DECORATOR_ATTRIBUTE" id="decoratorAttribute"/>
<!-- Add the DecoratingListener to the webapp to look for dynamic decorators -->
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.webapp.DecoratingListener">
<Arg><Ref refid="context" /></Arg>
<Arg type="String"><Ref refid="decoratorAttribute"/></Arg>
</New>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,14 @@
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Jetty setup to support Decoration of Listeners, Filters and Servlets within a deployed webapp.
This module uses the DecoratingListener to register an object set as a context attribute
as a dynamic decorator. This module sets the "org.eclipse.jetty.webapp.DecoratingListener"
context attribute with the name of the context attribute that will be listened to.
By default the attribute is "org.eclipse.jetty.webapp.decorator".
[depend]
deploy
[xml]
etc/jetty-decorate.xml

View File

@ -30,13 +30,13 @@
<table>
<tr>
<td>
<h2>examples ...</h2>
<h2>tests ...</h2>
<ul>
<li><a href="/test/">Test Jetty Webapp</a></li>
<li><a href="/async-rest/">Async Rest</a></li>
<li><a href="/test-spec/">Servlet 3.1 Test</a></li>
<li><a href="/test-jaas/">JAAS Test</a></li>
<li><a href="/test-jndi/">JNDI Test</a></li>
<li><a href="/test-spec/">Servlet 3.1 Test</a></li>
<li><a href="/async-rest/">Async Rest</a></li>
<li><a href="/oldContextPath/">Redirected Context</a></li>
</ul>
</td>

View File

@ -1,39 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-documentation</artifactId>
<name>Jetty :: Documentation</name>
<packaging>pom</packaging>
<packaging>jar</packaging>
<properties>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<html.common.directory>${project.build.directory}/html/common</html.common.directory>
<html.image.directory>${project.build.directory}/html/common/images</html.image.directory>
<javadoc.version>${project.version}</javadoc.version>
<asciidoctor.maven.plugin.version>1.5.7.1</asciidoctor.maven.plugin.version>
<asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version>
<asciidoctorj.epub.version>1.5.0-alpha.8.1</asciidoctorj.epub.version>
<asciidoctorj.version>1.5.8.1</asciidoctorj.version>
<jruby.version>1.7.27</jruby.version>
<web.resources.version>1.1</web.resources.version>
<web.resources.directory>${project.build.directory}/web-resources</web.resources.directory>
<!-- old properties -->
<html.directory>${project.build.directory}/current</html.directory>
<javadoc.version>${project.version}</javadoc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-jetty-web-resouces</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-web-resources</artifactId>
<version>${web.resources.version}</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${web.resources.directory}</outputDirectory>
</artifactItem>
</artifactItems>
<excludes>META-INF/**</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-assets</id>
<phase>process-resources</phase>
<id>copy-base-html-assets</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/main/resources</directory>
<directory>${web.resources.directory}/html</directory>
<includes>
<include>**</include>
</includes>
</resource>
</resources>
<outputDirectory>${html.directory}</outputDirectory>
<outputDirectory>${html.common.directory}</outputDirectory>
</configuration>
</execution>
</executions>
@ -41,108 +90,86 @@
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>${asciidoctor.maven.plugin.version}</version>
<configuration>
<attributes>
<JDURL>http://www.eclipse.org/jetty/javadoc/${javadoc.version}</JDURL>
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
<SRCDIR>${basedir}/..</SRCDIR>
<GITBROWSEURL>https://github.com/eclipse/jetty.project/tree/master</GITBROWSEURL>
<GITDOCURL>https://github.com/eclipse/jetty.project/tree/jetty-10.0.x-doc-refactor/jetty-documentation/src/main/asciidoc</GITDOCURL>
<DISTGUIDE>../distribution-guide/index.html</DISTGUIDE>
<EMBEDGUIDE>../embedded-guide/index.html</EMBEDGUIDE>
<QUICKGUIDE>../quickstart-guideindex.html</QUICKGUIDE>
<CONTRIBGUIDE>../contribution-guideindex.html</CONTRIBGUIDE>
<MVNCENTRAL>http://central.maven.org/maven2</MVNCENTRAL>
<VERSION>${project.version}</VERSION>
<TIMESTAMP>${maven.build.timestamp}</TIMESTAMP>
<docbits>${basedir}/src/main/java</docbits>
</attributes>
</configuration>
<executions>
<execution>
<id>output-html</id>
<phase>compile</phase>
<id>quickstart-guide</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>docbook</backend>
<doctype>book</doctype>
<backend>html5</backend>
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
<sourceDirectory>${basedir}/src/main/asciidoc/quickstart-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<attributes>
<sub-order>attributes+</sub-order>
<imagesdir />
<linkcss>true</linkcss>
<allow-uri-read>true</allow-uri-read>
<toc>true</toc>
<revnumber>${project.version}</revnumber>
<JDURL>http://www.eclipse.org/jetty/javadoc/${javadoc.version}</JDURL>
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
<SRCDIR>${basedir}/..</SRCDIR>
<GITBROWSEURL>https://github.com/eclipse/jetty.project/tree/jetty-10.0.x</GITBROWSEURL>
<MVNCENTRAL>https://repo1.maven.org/maven2</MVNCENTRAL>
<VERSION>${project.version}</VERSION>
</attributes>
<outputDirectory>${project.build.directory}/html/quickstart-guide</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<version>2.0.17</version>
<executions>
<execution>
<id>html</id>
<phase>compile</phase>
<id>distribution-guide</id>
<phase>generate-resources</phase>
<goals>
<goal>generate-html</goal>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<htmlStylesheet>css/docbook.css</htmlStylesheet>
<htmlCustomization>${project.basedir}/src/main/docbkx-stylesheet/html/docbook.xsl</htmlCustomization>
<preProcess>
<!-- pull over the images from the source material -->
<copy todir="target/docbkx/html/images" flatten="true">
<fileset dir="src/main/asciidoc">
<include name="**/*.png" />
<include name="**/*.jpg" />
<include name="**/*.svg" />
<include name="**/*.dot" />
</fileset>
</copy>
<copy todir="target/docbkx/html/images">
<fileset dir="src/main/docbkx-resources/images" />
</copy>
<copy todir="target/docbkx/html/css">
<fileset dir="src/main/docbkx-resources/css" />
</copy>
<copy todir="target/docbkx/html/fonts">
<fileset dir="src/main/docbkx-resources/fonts" />
</copy>
<copy todir="target/docbkx/html/js">
<fileset dir="src/main/docbkx-resources/js" />
</copy>
</preProcess>
<backend>html5</backend>
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
<sourceDirectory>${basedir}/src/main/asciidoc/distribution-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<outputDirectory>${project.build.directory}/html/distribution-guide</outputDirectory>
</configuration>
</execution>
<execution>
<id>contribution-guide</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html5</backend>
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
<sourceDirectory>${basedir}/src/main/asciidoc/contribution-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<outputDirectory>${project.build.directory}/html/contribution-guide</outputDirectory>
</configuration>
</execution>
<execution>
<id>embedded-guide</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html5</backend>
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
<sourceDirectory>${basedir}/src/main/asciidoc/embedded-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<outputDirectory>${project.build.directory}/html/embedded-guide</outputDirectory>
</configuration>
</execution>
</executions>
<configuration>
<!-- shared configuration -->
<sourceDirectory>${project.build.directory}/generated-docs</sourceDirectory>
<includes>index.xml</includes>
<generatedSourceDirectory>${project.build.directory}/docbkx/generated</generatedSourceDirectory>
<chunkedOutput>true</chunkedOutput>
<highlightSource>true</highlightSource>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.docbook</groupId>
<artifactId>docbook-xml</artifactId>
<version>5.1b4-all</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.xslthl</groupId>
<artifactId>xslthl</artifactId>
<version>2.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-xslt-tools</artifactId>
<version>1.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/html.xml</descriptor>
@ -160,79 +187,106 @@
</plugin>
</plugins>
</build>
<profiles>
<!--
Couple of different approaches to generating pdf's
-->
<profile>
<id>generate-pdf</id>
<id>alt-formats</id>
<build>
<plugins>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-pdf</id>
<phase>compile</phase>
<goals>
<goal>generate-pdf</goal>
</goals>
<configuration>
<includes>index.xml</includes>
<fop1Extensions>1</fop1Extensions>
<paperType>A4</paperType>
<foCustomization>src/main/docbkx-stylesheet/fo/docbook.xsl</foCustomization>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>net.sf.offo</groupId>
<artifactId>fop-hyph</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>${asciidoctor.maven.plugin.version}</version>
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>1.5.0-alpha.11</version>
<version>${asciidoctorj.pdf.version}</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-epub3</artifactId>
<version>${asciidoctorj.epub.version}</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>${jruby.version}</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId>
<version>${asciidoctorj.version}</version>
</dependency>
</dependencies>
<configuration>
<imagesdir />
<backend>docbook</backend>
<attributes>
<revnumber>${project.version}</revnumber>
<JDURL>http://www.eclipse.org/jetty/javadoc/${javadoc.version}</JDURL>
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
<SRCDIR>${basedir}/..</SRCDIR>
<GITBROWSEURL>https://github.com/eclipse/jetty.project/tree/master</GITBROWSEURL>
<MVNCENTRAL>http://central.maven.org/maven2</MVNCENTRAL>
<VERSION>${project.version}</VERSION>
</attributes>
</configuration>
<executions>
<execution>
<id>output-pdf</id>
<phase>generate-sources</phase>
<id>contribution-guide-pdf</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>pdf</backend>
<sourceHighlighter>rouge</sourceHighlighter>
<sourceDocumentName>index.adoc</sourceDocumentName>
<sourceHighlighter>coderay</sourceHighlighter>
<attributes>
<imagesdir />
<version>${project.version}</version>
<linkcss>true</linkcss>
<allow-uri-read>true</allow-uri-read>
<toc>true</toc>
<revnumber>${project.version}</revnumber>
<JDURL>http://download.eclipse.org/jetty/stable-9/apidocs</JDURL>
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
<SRCDIR>${basedir}/../jetty.project/</SRCDIR>
<GITBROWSEURL>https://github.com/eclipse/jetty.project/jetty-10.0.x</GITBROWSEURL>
<icons>font</icons>
<pagenums />
<toc />
<idprefix />
<idseparator>-</idseparator>
</attributes>
<sourceDirectory>${basedir}/src/main/asciidoc/contribution-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<outputFile>${project.build.directory}/pdf/contribution-guide/contribution-guide.pdf</outputFile>
</configuration>
</execution>
<execution>
<id>quickstart-distribution-guide-pdf</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>pdf</backend>
<sourceHighlighter>coderay</sourceHighlighter>
<attributes>
<icons>font</icons>
<pagenums />
<toc />
<idprefix />
<idseparator>-</idseparator>
</attributes>
<sourceDirectory>${basedir}/src/main/asciidoc/quickstart-distribution-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<outputFile>${project.build.directory}/pdf/quickstart-distribution-guide/quickstart-distribution-guide.pdf</outputFile>
</configuration>
</execution>
<execution>
<id>contribution-guide-epub</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>epub3</backend>
<sourceHighlighter>coderay</sourceHighlighter>
<sourceDirectory>${basedir}/src/main/asciidoc/contribution-guide</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<outputFile>${project.build.directory}/epub/contribution-guide/contribution-guide.epub</outputFile>
</configuration>
</execution>
</executions>
@ -241,4 +295,5 @@
</build>
</profile>
</profiles>
</project>

View File

@ -16,15 +16,10 @@
// ========================================================================
//
Jetty : The Definitive Reference
================================
:docinfo:
:revnumber: {VERSION}
:toc:
[[cg-introduction]]
== Welcome!
include::quick-start/part.adoc[]
include::configuring/part.adoc[]
include::administration/part.adoc[]
include::development/part.adoc[]
include::reference/part.adoc[]
Eclipse Jetty is an open source project with a long pedigree of contribution.
Starting over 20 years ago, Jetty has had many committers over the years and owes much of its success to the people that make up the community.
There are many ways that you may contribute to Jetty and the goal of this guide is help you get there!

View File

@ -0,0 +1,70 @@
//
// ========================================================================
// 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.
// ========================================================================
//
[[cg-community]]
== Participate in the Community
One of the best ways to start contribute and work with Eclipse Jetty is to get plugged into one of the existing communities.
In any one of these you will come across users of neophyte to expert experience and as time permits the developers of Jetty themselves.
[[cg-mailing-lists]]
=== Mailing Lists
Mailing lists are a wonderful way to interact with the community at large and for a certain class of issue have the best chances of achieving resolution. There are a number of mailing lists available within the Jetty project and a good rule of thumb to choose between the developer and user lists is to ask yourself if your question is broadly interesting to the community, use the users list and if narrowly focused on Jetty internals or minutiae, then the dev list might be a better option.
* Jetty Developers List
** Join - https://dev.eclipse.org/mailman/listinfo/jetty-dev
** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-dev/
* Jetty Users List
** Join - https://dev.eclipse.org/mailman/listinfo/jetty-users
** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-users/
* Jetty Announcements List
** Join - https://dev.eclipse.org/mailman/listinfo/jetty-announce
** Archives - http://dev.eclipse.org/mhonarc/lists/jetty-announce/
[[cg-irc]]
=== Internet Relay Chat - IRC
IRC has been around for decades and it is hard to beat a communication platform that has such support. Users are always welcome to come join our IRC channels and talk with us, other users, or just lurk. Please note that IRC is not necessarily an active support channel, if you have a question by all means ask it but there should be no expectation that you will immediately get a response. Your best bet is to ask and then lurk.
irc.freenode.org - #jetty::
Our primary location, we recommend that if your looking to find folks on IRC you try here.
[[cg-stackoverflow]]
=== Stack Overflow
From a simple support perspective it is hard to beat http://stackoverflow.com[StackOverflow] for interacting with a Jetty community. Numerous users have asked and had answered their questions on the platform by other users and developers alike.
* Check out the general https://stackoverflow.com/questions/tagged/jetty[Jetty] tag!
* The https://stackoverflow.com/questions/tagged/embedded-jetty[Embedded-Jetty] tag has a fair amount of traffic as well.
[[cg-issues]]
=== Github: Issues and Features
While not necessarily a support channel for solving a specific user problem, the issue tracker for Eclipse Jetty is a great location for addressing issues or suggesting features you want to see (or ideally contribute) in Jetty.
When you file a Github Issue in the Eclipse Jetty project there are a number of *labels* available and we encourage to you use them appropriately.
* http://github.com/eclipse/jetty.project[Issues at Github].
We have additionally identified and labeled a number of issues that may be appropriate for users of different levels of expertise that might want to contribute to Jetty but not have a specific goal or issue in mind.
* https://github.com/eclipse/jetty.project/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22[Help Wanted]
Long story short, if you are interested in getting involved with the community, there are a number of options available and you are encouraged to participate at whatever level you like!

View File

@ -0,0 +1,258 @@
//
// ========================================================================
// 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.
// ========================================================================
//
[[cg-source]]
== Participate in the Code
If you are more interested in digging into what makes Jetty tick then this some information that you will need to arm yourself with.
First we start with how to checkout and build Jetty, then on to our general coding standards followed by the actual patch contribution process.
[[cg-community-source]]
=== Source Control
The https://github.com/eclipse/jetty.project[Eclipse Jetty project] is located at https://github.com[Github] under the Eclipse Foundation https://github.com/eclipse[parent project]. There are a number of branches that are generally of interest.
.Active Eclipse Jetty Branches
[cols="3"]
|===
| https://github.com/eclipse/jetty.project/tree/jetty-10.0.x[jetty-10.0.x] | Development Branch for Jetty 10 | Java 9+
| https://github.com/eclipse/jetty.project/tree/jetty-9.4.x[jetty-9.4.x] | Maintenance Branch for Jetty 9.4 (default branch) | Java 8
| https://github.com/eclipse/jetty.project/tree/jetty-9.3.x[jetty-9.3.x] | Maintenance Branch for Jetty 9.3 | Java 8
| https://github.com/eclipse/jetty.project/tree/jetty-8.1.x[jetty-8.1.x] | Historical Branch for Jetty 8 | Java 7
https://github.com/eclipse/jetty.project/tree/jetty-7[jetty-7] | Historical Branch for Jetty 7 | Java 6
|===
If you are planning on working with a specific issue within Jetty it is important to target the correct branch for a pull request. Pull requests that are targeted at Maintenance Branches are typically merged forward into subsequent branches while historical branches are left alone merge wise. Depending on the nature of an issue a historical branch may have an issue cherrypicked forward, but maintenance releases are merged wholesale forward as a matter of project policy.
==== Primary SCM
The primary repository for Jetty is:
Jetty Project Repository::
https://github.com/eclipse/jetty.project
==== Secondary SCM
These are the URLs for Jetty-related code and metadata.
These are not needed to use Jetty; these are primarily of use for developers who are working with the open source project within Eclipse.
Administrative pom.xml file::
https://github.com/eclipse/jetty.parent
Build related artifacts that release separately, common assembly descriptors, remote resources, etc.::
https://github.com/eclipse/jetty.toolchain
Files associated with the development of Jetty -- code styles, formatting, iplogs, etc.::
http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git
[[t-contributing-build]]
=== Maven Build
Eclipse Jetty uses http://maven.apache.org/[Apache Maven] for managing the project metadata and controlling the build.
Building Jetty should simply be a matter of changing into the relevant directory and executing the following commands:
[source, screen, subs="{sub-order}"]
....
$ git clone https://github.com/eclipse/jetty.project.git
$ cd jetty.project
$ mvn install
....
All relevant dependencies should be downloaded into your local repository automatically and the build should proceed normally.
____
[NOTE]
Jetty has a great many test cases that run through the course of its build. Many of these tests spin up embedded instances of Jetty itself and it is not uncommon to see hundreds or more instances of Jetty start and stop during tests.
Periodically we find some test cases to be more time dependent than they should be and this results in intermittent test failures.
You can help track these down by opening an https://github.com/eclipse/jetty.project/issues[Issue].
____
[[cg-coding-standards]]
=== Coding Standards
Jetty uses number of conventions for its source code. The developers of Jetty use a variety of tooling and editors when developing Jetty so standards and conventions are important!
==== Intelli-J
The suggested configuration for Intelli-J when working with Jetty is available here: http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git/tree/idea-jetty-codestyle-settings.jar[Intelli-J Codestyle]
==== Eclipse
The Eclipse format configuration can be found here:
http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git/tree/jetty-eclipse-java-format.xml[Eclipse Java Formatting]
There are also some templates available for Eclipse here:
http://git.eclipse.org/c/jetty/org.eclipse.jetty.admin.git/tree/jetty-eclipse-codetemplates.xml[Eclipse Code Templates]
==== Code Conventions
The following is an example of the Java formatting and naming styles to apply to Jetty:
[source, java, subs="{sub-order}"]
----
import some.exact.ClassName; // GOOD
import some.wildcard.package.*; // BAD!
package org.always.have.a.package;
/* --------------------------------------------------------- */
/** Always have some javadoc
*/
class MyClassName
{
// indent by 4 spaces.
// use spaced to indent
// The code must format OK with default tabsize of 8.
private static final int ALL_CAPS_FOR_PUBLIC_CONSTANTS=1;
// Field prefixed with __ for static of _ for normal fields.
// This convention is no longer mandatory, but any given
// class should either consistently use this style or not.
private static String __staticField;
private Object _privateField;
// use getters and setters rather than public fields.
public void setPrivateField(Object privateField)
{
_privateField=privateField;
}
public Object getPrivateField()
{
return _privateField;
}
public void doSomething()
throws SomeException
{
Object local_variable = _privateField;
if (local_variable==null)
{
// do Something
}
}
}
----
While Eclipse Jetty is an open source project it is also a member of the Eclipse Foundation which carries along some additional responsibilities.
Intellectual Property is a hallmark concern of the Eclipse Foundation so you are encouraged to understand what that entails before diving in.
As much as we would like to accept a tremendous pull request, without the proper chain of events being completed our hands are tied.
That being said, the steps are not particularly onerous and we are happy to work with you to get them accomplished.
[[cg-patches]]
=== Contributing Patches
We love seeing people contribute patches to the Jetty project and the process is relatively simple.
The requirements to commit are modest but very important to the Eclipse Foundation and the intellectual property of the open source project.
The following is the general process by which we operate.
* You must have a signed Eclipse Contributor Agreement.
* This agreement must be under the _same_ email address as the Git pull request originates from.
* The commit must be signed.
* When the pull request is made, a git-hook will validate the email address.
** If the result is a green checkbox then the Jetty committers can review the pull request.
** If the result is a red X then there is absolutely nothing the Jetty committers can do to accept the commit at this point.
* This may not be the final form a commit will take, there may be some back and forth and you may be asked to re-issue a pull request.
Not everything is specifically relevant since we are at GitHub but the crux of things are detailed there.
The ECA is *critically* important to the process.
[[cg-contributing-eca]]
==== Sign an Eclipse Contributor Agreement (ECA)
The Eclipse Foundation has a strong Intellectual Property policy which tracks contributions in detail to ensure that:
1. Did the contributor author 100% of the content?
2. Does the contributor have the rights to contribute this content to Eclipse?
3. Is the contribution under the projects license(s) (e.g. EPL)
A contributor needs to e-sign a Eclipse Contributor Agreement (for more explanation see the http://www.eclipse.org/legal/ecafaq.php[Eclipse ECA FAQ] ) regardless of how their contribution patch is provided.
You can familiarize yourself with the Eclipse wiki page at http://wiki.eclipse.org/Development_Resources/Contributing_via_Git[Contributing via Git].
In order to have a pull request accepted by any Eclipse project you *must* complete this agreement.
____
[TIP]
Log into the https://www.eclipse.org[Eclipse home page] (you will need to create an account with the Eclipse Foundation if you have not already done so), click on "Eclipse ECA", and complete the form.
Be sure to use the _same email address_ when you create any Git commit records.
____
[[t-contributing-git-config]]
==== Configuring Git
GitHub has copious amounts of quality documentation on how to interact with the system and you will minimally need to configure the user.email property.
Check out the following link:https://help.github.com/articles/setting-your-email-in-git[guide on GitHub] for more information.
[[t-contributing-making-the-commit]]
==== Making the Commit
When making the commit for the pull request it is _vital_ that you "sign-off" on the commit using `git commit -s` option.
Without this sign-off, your patch cannot be applied to the Jetty repository because it will be rejected.
You can check out the link:https://help.github.com/articles/signing-tags-using-gpg[guide at Github] for more information.
____
[TIP]
One way to think of this is that when you sign the ECA you are indicating that you are free to contribute to eclipse, but that doesn't mean everything you ever do can be contributed.
Using the commit signing mechanism indicates that your commit is under the auspices of your agreement.
____
If a pull request is for a particular issue in our repository then the format of the commit message is important.
The message should follow the form "Issue #123 <description of the commit>".
When the Jetty project runs releases we have an automated process that scans for commits with this format for inclusion in our VERSION.txt file.
[source, screen]
----
> git commit -s -m "Issue #123 resolving the issue by adding widget"
----
[[cg-the-pull-request]]
==== The Pull Request
Pull requests are very much a GitHub process so best link:https://help.github.com/articles/creating-a-pull-request[explained by Github].
[[cg-our-policies]]
==== Our Policies
We wholeheartedly welcome contributions to Jetty and will do our best to process them in a timely fashion.
While not every contribution will be accepted, our commitment is to work with interested parties on the things they care about.
With that in mind, we can only handle pull requests with actively engaged parties.
We reserve the right to abandon pull requests whose authors do no respond in a timely fashion.
We will generally adhere to the following time frames for contributions:
* Invalid Pull Requests - 1 week
** These pull requests do not follow the contribution requirements for some reason, be it missing contributor agreement or the wrong email.
** We will try and follow up with the pull request author to resolve the issue but much of this is out of our hands and are between committer and the Eclipse Foundation.
** If we do not hear from the contributor after a week we will close the pull request.
* Valid Pull Requests - 2 weeks
** These pull requests have a green check mark after the commit title.
** If the pull request can be immediately applied we will do so.
** There may need to be some conversation on the issue in which case a committer will follow up with the author in the pull request.
** If the original contributor does not respond within 2 weeks we may close the commit.
** If we see value in the commit yet the author has not responded after 2 weeks we may make some variation of the commit ourselves.

View File

@ -0,0 +1,211 @@
//
// ========================================================================
// 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.
// ========================================================================
//
[[cg-documentation]]
== Participate in the Documentation
Another wonderful way to help with Eclipse Jetty is to help contribute to our corpus of documentation.
We have taken every every to reduce the barriers to contributing to our documentation and many contributors find our documentation as a low key way to participate and learn the process.
[[cg-documentation-format]]
=== Source Control and Maven Build.
The Jetty documentation is a module within the overall Jetty project and is build as a part of the standard build process.
As such to checkout the documentation you can follow the link:#t-community-source[same process] as checking out Jetty itself.
As a part of the main Jetty project the documentation is build through the link:#t-community-build[same process] as Jetty.
However, it is a more independent module and can be worked with much simpler by building strictly the jetty-documentation module.
[source, screen, subs="{sub-order}"]
....
$ git clone https://github.com/eclipse/jetty.project.git
$ cd jetty.project/jetty-documentation
$ mvn install
....
While maven is running you may see a lot of files being downloaded.
If you are not familiar with Maven you are seeing Maven set up the execution environment for generating the documentation.
The build is finished once you see a message akin to:
[source, screen, subs="{sub-order}"]
....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.272 s
[INFO] Finished at: 2018-04-09T13:06:10-05:00
[INFO] Final Memory: 74M/247M
[INFO] ------------------------------------------------------------------------
....
At this point you open your web browser and view the produced documentation (under the target/html directory)!
[TIP]
====
Pay close attention to the build output for lines like:
....
asciidoctor: WARNING: 9-jsp.adoc: line 0: id assigned to section already in use: ag-http2
....
This indicates that an ID is being reused and should be resolved or else deep linking will work correctly.
====
[[cg-documentation-render]]
=== The Render Chain Explained
The Jetty documentation is all written in https://asciidoctor.org/docs/user-manual/[asciidoc].
The maven build uses the asciidoctor-maven-plugin to process specific index.adoc[] files which act as the root structure for various guides (Administrative, Developer, QuickStart, etc).
The main pom.xml file also provides a maven profile which when activated can produce the guides in both pdf and epub formats.
[[cg-documentation-structure]]
=== Project Structure
It may be useful to understand the layout of the documentation before digging into the details.
The following table lists some helpful directories and their purpose.
.Documentation Layout
[cols="2"]
|===
| src/main/asciidoc | root of asciidoc layout
| src/main/asciidoc/*-guide | root structure for each individual guide
| target/html | post build, where the rendered html files will appear
| target/pdf | post build (-Palt-formats) pdf files
| target/epub | post build (-Palt-formats) epub files
|===
[[cg-documentation-guide]]
=== Guide Structure
Each guide starts with an *index.adoc* file which acts as the point of origin for the document structure.
It will declare useful metadata for the proper rendering of the overall guide and use the asciidoc *include* directive to pull in content for each chapter which are conveniently named by chapter number and name.
[[cg-documentation-conventions]]
=== Conventions
Below is list of conventions that should be followed when developing documentation within this framework.
These are not set in stone but you are encouraged to follow them when possible.
ventilated prose::
Each sentence should generally be on its own line with a hard return at the end of the line.
Asciidoc rendering does not treat this style as separate lines and will produce paragraphs correctly.
The primary benefit is that you can easily see changes between versions of files, and it makes it trivial to quickly look through a pull request.
Additional benefits are the ability to comment out a sentence mid paragraph or move sentences around within a paragraph.
Enabling Soft Line Wrap in your favorite editor can make this a bit easier to stomach.
id's::
Critically important for being able to generate url's that can be used in a persistent fashion.
Without sane id's the chapters and sections will have generated id which are rooted in some obscure location based
voodoo.
A url using these 'e12c8673' type links will not be durable across generations of the documentation.
These id's need to be used on chapters and sections two deep, and anywhere that you intend to cross link deeper.
The id values go into a global namespace so they must be unique across the entire document or the last example will win and any cross links will go there.
Links inside of a guides narrative structure should be prefixed with an acronym (contributor guide = cg-) while any links within the topics structure should be prefixed with t- and ideally the name of the topic it corresponds to.
link vs xref::
The `link:` item should be generally used for linking around the document when you want to choose the text that will be rendered in the link itself.
However if you are linking to a section and want to use the title itself as the link text, use the `xref:` tag without the hashmark in front of the link id.
cross guide linkage::
To link to another guide it is best to use a property to address it. The following guide shortcuts are provided.
* DISTGUIDE
* EMBEDGUIDE
* QUICKGUIDE
* CONTRIBUIDE
....
This is a test to link to link:{DISTGUIDE}[Distribution Guide]
This is a test to deep link to link:{DISTGUIDE}#startup[Distribution Guide Deep Link]
....
This is a test to link to link:{DISTGUIDE}[Distribution Guide]
This is a test to deep link to link:{DISTGUIDE}#startup[Distribution Guide Deep Link]
images::
Images are placed in the `/images*` directory of the guide they should appear in and then referenced with a `image:` tag.
....
image:small_powered_by.gif[image,width=340]
....
image:small_powered_by.gif[image,width=340]
version differences::
In general differences in functionality within a release should go into nested sections and use titles like 'Prior to: ##' or 'In version: ##'.
license blocks::
Each adoc file should contain the license block that exists in the index.adoc file and a copy has been added to the bottom of this page as well for reference.
....
//
// ========================================================================
// Copyright (c) 1995-2018 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.
// ========================================================================
//
....
Some admonition examples:
______________________________________________
[NOTE]
A note about the previous case to be aware of.
______________________________________________
________________________________________
[IMPORTANT]
Important notes are marked with an icon.
________________________________________
________________________________
[TIP]
Tips that make your life easier.
________________________________
_______________________________________________________
[CAUTION]
Places where you have to be careful what you are doing.
_______________________________________________________
__________________________________________________________________________________________________________________
[WARNING]
Where extreme care has to be taken. Data corruption or other nasty
things may occur if these warnings are ignored.
__________________________________________________________________________________________________________________
==== Oddities
* If an included file ends with a list entry, it needs to have two empty lines at the end of the file in order for the section rendering to work correctly.

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// 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.
// ========================================================================
//
[[cg-security]]
== Security
[[cg-security-reporting]]
=== Reporting Security Issues
There are a number of avenues for reporting security issues to the Jetty project available.
If the issue is directly related to Jetty itself then reporting to the Jetty developers is encouraged.
The most direct method is to send e-mail to _security@webtide.com_.
Since Webtide is comprised of the active committers of the Jetty project this is our preferred reporting method.
We are generally flexible in how we work with reporters of security issues from an attribution perspective but we reserve the right to act in the interests of the Jetty project in all circumstances.
If the issue is related to Eclipse or its Jetty integration then we encourage you to reach out to _security@eclipse.org_.
If the issue is related to some third party integration with Jetty we are happy to work with you to identify the proper entity and worth with them to properly address any issue so either of the above outreaches is fine.
We prefer that security issues are reported directly to Jetty developers via email as opposed through GitHub Issues since there is currently no facility for _private_ issues. There is an https://github.com/isaacs/github/issues/37[unofficial thread] for this support at github you are welcome to follow along on.

View File

@ -16,19 +16,16 @@
// ========================================================================
//
[[jetty-admin-guide]]
= Jetty Administration Guide
[[cg-conclusion]]
== Thank you!
include::startup/chapter.adoc[]
include::sessions/chapter.adoc[]
include::logging/chapter.adoc[]
include::jndi/chapter.adoc[]
include::annotations/chapter.adoc[]
include::jmx/chapter.adoc[]
include::alpn/chapter.adoc[]
include::http2/chapter.adoc[]
include::fastcgi/chapter.adoc[]
include::extras/chapter.adoc[]
include::runner/chapter.adoc[]
include::tuning/chapter.adoc[]
Your interest in contributing to Eclipse Jetty is welcome and if there is anything you feel this guide is lacking please let us know.
Feel free to open an Issue as described in this document and perhaps even contribute your suggestion to {GITDOCURL}/contribution-guide[this document] itself!
[[cg-contributing-guides]]
Our current guides are:
* {GUIDEBASEURL}/quickstart/[Distribution Quickstart Guide]
* {GUIDEBASEURL}/contribution/[Contribution Guide]

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,67 @@
//
// ========================================================================
// 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.
// ========================================================================
//
:doctitle: Eclipse Jetty: Contribution Guide
:author: Jetty Developers
:email: jetty-dev@eclipse.org
:revnumber: 1.0
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Contribution Guide
:toc-image: ../../common/images/jetty-logo.svg
:toc-image-url: /jetty/index.html
:breadcrumb: Home:../index.html | Contribution Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
//:docinfodir: {DOCINFODIR}/documentation
//:docinfo1:
// html specific directives
ifdef::backend-html5[]
:safe-mode-unsafe:
:stylesdir: ../common/css
:stylesheet: jetty.css
:linkcss:
:scriptsdir: ../common/js
endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
// suppress document footer generation
//:nofooter:
// suppress Eclipse footer
:no-eclipse-footer:
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true
// use fonts for admonitions
:icons: font
// suppress automatic id generation
:sectids!:
include::1-introduction.adoc[]
include::2-community.adoc[]
include::3-source.adoc[]
include::4-documentation.adoc[]
include::5-security.adoc[]
include::6-conclusion.adoc[]

View File

@ -119,6 +119,13 @@ By default Jetty will call them in the following order:
As is the case with annotation scanning, the link:#using-extra-classpath-method[extraClasspath] is fully considered for `ServletContainerInitializer` callbacks. `ServletContainerInitializer` derived from a classes directory on the `extraClasspath` and jars from an `extraClasspath` for the webapp are called in step 2 and 3, respectively.
____
[NOTE]
As of Jetty-9.4.4, unless the `web.xml` is version 3.0 or greater, only `ServletContainerInitializers` that are on the container classpath will be discovered.
Users wishing to use `ServletContainerInitializers` from within the webapp with older versions of `web.xml` must either upgrade their `web.xml` version, or call `WebAppContext.setConfigurationDiscovered(true)` either programmatically or in xml.
Upgrading the `web.xml` version is preferable.
____
===== Controlling the order of ServletContainerInitializer invocation
If you need `ServletContainerInitializer` classes called in a specific order that is different from that outlined above, you can use the link:#context_attributes[context attribute] `org.eclipse.jetty.containerInitializerOrder`.

View File

@ -361,7 +361,7 @@ If you imported the key and certificate originally using the PKCS12 method, use
The `keystore` only contains the server's private key and certificate.
[[img-certificate-chain]]
image::images/certificate-chain.png[title="Certificate chain", alt="Certificate chain"]
image::certificate-chain.png[title="Certificate chain", alt="Certificate chain"]
[literal]
.The structure of KeyStore file:

View File

@ -27,7 +27,7 @@ The Jetty distribution contains example `DeploymentManager` configurations to de
The `DeploymentManager` is the heart of the typical webapp deployment mechanism; it operates as a combination of an Application LifeCycle Graph, Application Providers that find and provide Applications into the Application LifeCycle Graph, and a set of bindings in the graph that control the deployment process.
image:images/Jetty_DeployManager_DeploymentManager_Roles.png[image,width=195]
image:Jetty_DeployManager_DeploymentManager_Roles.png[image,width=195]
[[udm-application-providers]]
==== Application Providers
@ -40,7 +40,7 @@ The main `AppProvider` with the Jetty distribution is the link:{JDURL}/org/eclip
The core feature of the `DeploymentManager` is the link:{JDURL}/org/eclipse/jetty/deploy/AppLifeCycle.html[Application LifeCycle Graph].
image:images/Jetty_DeployManager_AppLifeCycle-1.png[image,width=340]
image:Jetty_DeployManager_AppLifeCycle-1.png[image,width=340]
The nodes and edges of this graph are pre-defined in Jetty along the most common actions and states found.
These nodes and edges are not hardcoded; they can be adjusted and added to depending on need (for example, any complex requirements for added workflow, approvals, staging, distribution, coordinated deploys for a cluster or cloud, etc.).
@ -68,7 +68,7 @@ There are four default bindings:
* link:{JDURL}/org/eclipse/jetty/deploy/bindings/StandardStopper.html[`StandardStopper`] — Stops the ContextHandler and stops accepting incoming requests.
* link:{JDURL}/org/eclipse/jetty/deploy/bindings/StandardUndeployer.html[`StandardUndeployer`] — Removes the ContextHandler from Jetty.
image:images/Jetty_DeployManager_DefaultAppLifeCycleBindings.png[image,width=851]
image:Jetty_DeployManager_DefaultAppLifeCycleBindings.png[image,width=851]
A fifth, non-standard binding, called link:{JDURL}/org/eclipse/jetty/deploy/bindings/DebugBinding.html[DebugBinding], is also available for debugging reasons; it logs the various transitions through the Application LifeCycle.

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