├── .git-blame-ignore-revs ├── .github ├── FUNDING.yml ├── dependabot.yml ├── release-drafter.yml └── workflows │ └── cd.yml ├── .gitignore ├── .mvn ├── extensions.xml ├── jvm.config └── maven.config ├── CHANGELOG-2.x.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Jenkinsfile ├── LICENSE.md ├── NOTICE ├── README.md ├── TODO.txt ├── docs ├── class-loading.md ├── close.md ├── configuration.md ├── inbound-agent.md ├── jenkins-specifics.md ├── logging.md ├── no_proxy.md ├── protocol-stack.dot ├── protocol-stack.svg ├── protocols.md ├── remoting-kafka-architecture.png ├── tcpAgentStatus.png ├── troubleshooting.md └── workDir.md ├── pom.xml └── src ├── assembly ├── agent-load-test.xml └── agent-load-test │ ├── LICENSE.md │ ├── NOTICE │ ├── README.md │ ├── agent-load-test │ └── agent-load-test.bat ├── filter └── resources │ ├── hudson │ └── remoting │ │ └── hudson-version.properties │ └── jenkins │ └── remoting │ └── jenkins-version.properties ├── main └── java │ ├── hudson │ └── remoting │ │ ├── AbstractByteArrayCommandTransport.java │ │ ├── AbstractByteBufferCommandTransport.java │ │ ├── AbstractSynchronousByteArrayCommandTransport.java │ │ ├── AsyncFutureImpl.java │ │ ├── Asynchronous.java │ │ ├── AtmostOneThreadExecutor.java │ │ ├── Base64.java │ │ ├── BinarySafeStream.java │ │ ├── Callable.java │ │ ├── CallableDecoratorAdapter.java │ │ ├── CallableDecoratorList.java │ │ ├── CallableFilter.java │ │ ├── Capability.java │ │ ├── Channel.java │ │ ├── ChannelBuilder.java │ │ ├── ChannelClosedException.java │ │ ├── ChannelProperty.java │ │ ├── Checksum.java │ │ ├── ChunkHeader.java │ │ ├── ChunkedCommandTransport.java │ │ ├── ChunkedInputStream.java │ │ ├── ChunkedOutputStream.java │ │ ├── ClassFilter.java │ │ ├── ClassLoaderHolder.java │ │ ├── ClassicCommandTransport.java │ │ ├── Command.java │ │ ├── CommandTransport.java │ │ ├── DaemonThreadFactory.java │ │ ├── DelegatingCallable.java │ │ ├── DelegatingExecutorService.java │ │ ├── DiagnosedStreamCorruptionException.java │ │ ├── DumbClassLoaderBridge.java │ │ ├── Engine.java │ │ ├── EngineListener.java │ │ ├── EngineListenerAdapter.java │ │ ├── EngineListenerSplitter.java │ │ ├── ErrorPropagatingOutputStream.java │ │ ├── ExportTable.java │ │ ├── FastPipedInputStream.java │ │ ├── FastPipedOutputStream.java │ │ ├── FileSystemJarCache.java │ │ ├── FlightRecorderInputStream.java │ │ ├── Future.java │ │ ├── FutureAdapter.java │ │ ├── GCCommand.java │ │ ├── HexDump.java │ │ ├── IChannel.java │ │ ├── IReadResolve.java │ │ ├── ImportedClassLoaderTable.java │ │ ├── InitializeJarCacheMain.java │ │ ├── InterceptingExecutorService.java │ │ ├── InternalCallable.java │ │ ├── JarCache.java │ │ ├── JarCacheSupport.java │ │ ├── JarLoader.java │ │ ├── JarLoaderImpl.java │ │ ├── JarURLValidator.java │ │ ├── Launcher.java │ │ ├── LocalChannel.java │ │ ├── MimicException.java │ │ ├── MultiClassLoaderSerializer.java │ │ ├── NamingThreadFactory.java │ │ ├── NoProxyEvaluator.java │ │ ├── ObjectInputStreamEx.java │ │ ├── PingThread.java │ │ ├── Pipe.java │ │ ├── PipeWindow.java │ │ ├── PipeWriter.java │ │ ├── PreloadJarTask.java │ │ ├── PreloadJarTask2.java │ │ ├── ProxyException.java │ │ ├── ProxyInputStream.java │ │ ├── ProxyOutputStream.java │ │ ├── ProxyWriter.java │ │ ├── RemoteClassLoader.java │ │ ├── RemoteInputStream.java │ │ ├── RemoteInvocationHandler.java │ │ ├── RemoteOutputStream.java │ │ ├── RemoteWriter.java │ │ ├── RemotingSystemException.java │ │ ├── Request.java │ │ ├── RequestAbortedException.java │ │ ├── RequiredRoleCheckerWrapper.java │ │ ├── ResourceImageBoth.java │ │ ├── ResourceImageDirect.java │ │ ├── ResourceImageInJar.java │ │ ├── ResourceImageRef.java │ │ ├── Response.java │ │ ├── SingleLaneExecutorService.java │ │ ├── SocketChannelStream.java │ │ ├── SocketInputStream.java │ │ ├── SocketOutputStream.java │ │ ├── StandardOutputStream.java │ │ ├── SynchronousCommandTransport.java │ │ ├── SynchronousExecutorService.java │ │ ├── TeeOutputStream.java │ │ ├── URLDeserializationHelper.java │ │ ├── URLish.java │ │ ├── UnexportCommand.java │ │ ├── UserRequest.java │ │ ├── Util.java │ │ ├── VirtualChannel.java │ │ ├── Which.java │ │ ├── forward │ │ ├── CopyThread.java │ │ ├── Forwarder.java │ │ ├── ForwarderFactory.java │ │ ├── ListeningPort.java │ │ ├── PortForwarder.java │ │ └── package-info.java │ │ ├── jnlp │ │ ├── Main.java │ │ └── package-info.java │ │ └── package-info.java │ └── org │ └── jenkinsci │ └── remoting │ ├── CallableDecorator.java │ ├── ChannelStateException.java │ ├── DurationOptionHandler.java │ ├── Role.java │ ├── RoleChecker.java │ ├── RoleSensitive.java │ ├── SerializableOnlyOverRemoting.java │ ├── engine │ ├── HostPort.java │ ├── Jnlp4ConnectionState.java │ ├── JnlpAgentEndpoint.java │ ├── JnlpAgentEndpointConfigurator.java │ ├── JnlpAgentEndpointResolver.java │ ├── JnlpClientDatabase.java │ ├── JnlpConnectionState.java │ ├── JnlpConnectionStateListener.java │ ├── JnlpEndpointResolver.java │ ├── JnlpProtocol4Handler.java │ ├── JnlpProtocol4ProxyHandler.java │ ├── JnlpProtocolHandler.java │ ├── JnlpProtocolHandlerFactory.java │ └── WorkDirManager.java │ ├── nio │ ├── Closeables.java │ ├── FifoBuffer.java │ ├── NioChannelBuilder.java │ ├── NioChannelHub.java │ ├── SelectableFileChannelFactory.java │ └── SelectorThreadOnly.java │ ├── org │ └── apache │ │ └── commons │ │ ├── net │ │ └── util │ │ │ └── SubnetUtils.java │ │ └── validator │ │ └── routines │ │ ├── InetAddressValidator.java │ │ └── RegexValidator.java │ ├── protocol │ ├── ApplicationLayer.java │ ├── FilterLayer.java │ ├── IOHub.java │ ├── IOHubReadyListener.java │ ├── IOHubRegistrationCallback.java │ ├── IOHubRegistrationFutureAdapterImpl.java │ ├── NetworkLayer.java │ ├── ProtocolLayer.java │ ├── ProtocolStack.java │ ├── cert │ │ ├── BlindTrustX509ExtendedTrustManager.java │ │ ├── DelegatingX509ExtendedTrustManager.java │ │ ├── PublicKeyMatchingX509ExtendedTrustManager.java │ │ └── ValidityCheckingX509ExtendedTrustManager.java │ └── impl │ │ ├── AckFilterLayer.java │ │ ├── AgentProtocolClientFilterLayer.java │ │ ├── BIONetworkLayer.java │ │ ├── ChannelApplicationLayer.java │ │ ├── ConnectionHeaders.java │ │ ├── ConnectionHeadersFilterLayer.java │ │ ├── ConnectionRefusalException.java │ │ ├── NIONetworkLayer.java │ │ ├── PermanentConnectionRefusalException.java │ │ └── SSLEngineFilterLayer.java │ └── util │ ├── AnonymousClassWarnings.java │ ├── ByteBufferPool.java │ ├── ByteBufferQueue.java │ ├── ByteBufferQueueInputStream.java │ ├── ByteBufferQueueOutputStream.java │ ├── ByteBufferUtils.java │ ├── DirectByteBufferPool.java │ ├── DurationFormatter.java │ ├── DurationStyle.java │ ├── ExecutorServiceUtils.java │ ├── FastByteBufferQueueInputStream.java │ ├── IOUtils.java │ ├── KeyUtils.java │ ├── ListenableFuture.java │ ├── LoggingChannelListener.java │ ├── PathUtils.java │ ├── SettableFuture.java │ ├── ThrowableUtils.java │ ├── VersionNumber.java │ └── https │ ├── NoCheckHostnameVerifier.java │ └── NoCheckTrustManager.java ├── spotbugs └── excludesFilter.xml └── test ├── java ├── JarCertDump.java ├── OISInterception.java ├── TrafficAnalyzer.java ├── hudson │ └── remoting │ │ ├── AbstractNioChannelRunner.java │ │ ├── BinarySafeStreamTest.java │ │ ├── CallableBase.java │ │ ├── ChannelFilterTest.java │ │ ├── ChannelRunner.java │ │ ├── ChannelRunners.java │ │ ├── ChannelTest.java │ │ ├── ChecksumTest.java │ │ ├── ChunkedInputStreamTest.java │ │ ├── ClassFilterTest.java │ │ ├── ClassRemoting2Test.java │ │ ├── ClassRemotingTest.java │ │ ├── Copier.java │ │ ├── DeadRemoteOutputStreamTest.java │ │ ├── DefaultClassFilterTest.java │ │ ├── DiagnosedStreamCorruptionExceptionTest.java │ │ ├── DualSideChannelRunner.java │ │ ├── DummyClassLoader.java │ │ ├── DummyClassLoaderTest.java │ │ ├── EngineTest.java │ │ ├── ExportTableTest.java │ │ ├── FileSystemJarCacheTest.java │ │ ├── FlightRecorderInputStreamTest.java │ │ ├── ForkEBCDICRunner.java │ │ ├── ForkRunner.java │ │ ├── HexDumpTest.java │ │ ├── InProcessCompatibilityRunner.java │ │ ├── InProcessRunner.java │ │ ├── LauncherTest.java │ │ ├── NioPipeRunner.java │ │ ├── NioSocketRunner.java │ │ ├── NoProxyEvaluatorTest.java │ │ ├── NonSerializableExceptionTest.java │ │ ├── PipeTest.java │ │ ├── PipeWriterTest.java │ │ ├── PipeWriterTestChecker.java │ │ ├── PrefetchTest.java │ │ ├── PrefetchingTest.java │ │ ├── ProxyExceptionTest.java │ │ ├── ProxyWriterTest.java │ │ ├── RegExpBenchmark.java │ │ ├── RemoteInputStreamTest.java │ │ ├── RemoteInvocationHandlerTest.java │ │ ├── SimpleTest.java │ │ ├── SingleLaneExecutorServiceTest.java │ │ ├── TeeOutputStreamTest.java │ │ ├── TestCallable.java │ │ ├── TestLinkage.java │ │ ├── TestStaticGetResources.java │ │ ├── TestStaticResourceReference.java │ │ ├── TrafficAnalyzer.java │ │ ├── URLDeserializatinHelperTest.java │ │ ├── WithRunner.java │ │ ├── pipe │ │ ├── RandomWorkload.java │ │ └── Workload.java │ │ ├── throughput │ │ ├── DumbReceiver.java │ │ ├── DumbSender.java │ │ ├── Receiver.java │ │ └── Sender.java │ │ └── util │ │ └── GCTask.java └── org │ └── jenkinsci │ └── remoting │ ├── engine │ ├── HandlerLoopbackLoadStress.java │ ├── HostPortTest.java │ ├── JnlpAgentEndpointResolverTest.java │ ├── JnlpProtocolHandlerTest.java │ ├── PropertiesStringMatcher.java │ ├── WorkDirManagerRule.java │ └── WorkDirManagerTest.java │ ├── nio │ ├── FifoBufferTest.java │ ├── FlushEveryByteStream.java │ ├── Main.java │ ├── SocketClientMain.java │ └── SocketServerMain.java │ ├── org │ └── apache │ │ └── commons │ │ ├── net │ │ └── util │ │ │ └── SubnetUtilsTest.java │ │ └── validator │ │ └── routines │ │ ├── InetAddressValidatorTest.java │ │ └── RegexValidatorTest.java │ ├── protocol │ ├── IOBufferMatcher.java │ ├── IOBufferMatcherLayer.java │ ├── IOHubRule.java │ ├── IOHubTest.java │ ├── NetworkLayerFactory.java │ ├── ProtocolStackImplTest.java │ ├── ProtocolStackLoopbackLoadStress.java │ ├── ProtocolStackTest.java │ ├── Repeat.java │ ├── RepeatRule.java │ ├── cert │ │ ├── BlindTrustX509ExtendedTrustManagerTest.java │ │ ├── DHKeyPairRule.java │ │ ├── DSAKeyPairRule.java │ │ ├── ECKeyPairRule.java │ │ ├── KeyPairRule.java │ │ ├── PublicKeyMatchingX509ExtendedTrustManagerTest.java │ │ ├── RSAKeyPairRule.java │ │ ├── SSLContextRule.java │ │ ├── ValidityCheckingX509ExtendedTrustManagerTest.java │ │ └── X509CertificateRule.java │ └── impl │ │ ├── AckFilterLayerTest.java │ │ ├── BatchSendBufferingFilterLayer.java │ │ ├── ConnectionHeadersFilterLayerTest.java │ │ ├── ConnectionHeadersTest.java │ │ ├── HoldFilterLayer.java │ │ ├── NetworkLayerTest.java │ │ ├── NoOpFilterLayer.java │ │ ├── SSLEngineFilterLayerTest.java │ │ └── SequentialSender.java │ └── util │ ├── ByteBufferQueueInputStreamTest.java │ ├── ByteBufferQueueOutputStreamTest.java │ ├── ByteBufferQueueTest.java │ ├── DurationFormatterTest.java │ ├── DurationStyleTest.java │ ├── FastByteBufferQueueInputStreamTest.java │ ├── SettableFutureTest.java │ └── VersionNumberTest.java └── resources └── hudson └── remoting ├── embedded_doctype.jnlp ├── lol.jnlp ├── note.dtd ├── test.jnlp ├── xxe_file.jnlp └── xxe_http.jnlp /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # .git-blame-ignore-revs 2 | # Format repository with Spotless (#751) 3 | 3ea58fc6d3356698eb8b14210135149b8f1e6018 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | community_bridge: jenkins 2 | custom: ["https://jenkins.io/donate/#why-donate"] 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: github-actions 9 | directory: / 10 | schedule: 11 | interval: weekly -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | # Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins 2 | 3 | name: cd 4 | on: 5 | workflow_dispatch: 6 | check_run: 7 | types: 8 | - completed 9 | 10 | jobs: 11 | maven-cd: 12 | uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1 13 | secrets: 14 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 15 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.ipr 3 | *.iws 4 | .idea 5 | target 6 | /.classpath 7 | /.project 8 | /.settings 9 | .fbExcludeFilterFile 10 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.jenkins.tools.incrementals 4 | git-changelist-maven-extension 5 | 1.8 6 | 7 | 8 | -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | -Xmx256m -XX:+HeapDumpOnOutOfMemoryError -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | -Dchangelist.format=%d.v%s -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ===== 3 | 4 | Remoting library follows the common contribution rules/recommendations in the Jenkins project. 5 | These recommendations are available [here](https://wiki.jenkins-ci.org/display/JENKINS/Beginners+Guide+to+Contributing). 6 | 7 | ### Proposing changes 8 | 9 | * Create pull requests (PRs) against the master branch. 10 | * Bug fixes or performance improvements may be a subject for backporting to Remoting 2.x. 11 | If you need it, please mention it in the pull request or JIRA ticket. 12 | 13 | ### Q&A 14 | 15 | Use the [Jenkins developer mailing list](https://groups.google.com/g/jenkinsci-dev) to sync-up with remoting developers. 16 | In the case of the mailing list, it's a good practice to CC maintainers directly. 17 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | /* 2 | * While this is not a plugin, it is much simpler to reuse the pipeline code for CI. This allows for 3 | * easy Linux/Windows testing and produces incrementals. The only feature that relates to plugins is 4 | * allowing one to test against multiple Jenkins versions. 5 | */ 6 | buildPlugin(timeout: 20, useContainerAgent: true, configurations: [ 7 | [platform: 'linux', jdk: 21], 8 | [platform: 'windows', jdk: 17], 9 | ]) 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Copyright (c) 2004-2016, Sun Microsystems, Inc., Kohsuke Kawaguchi, Daniel Dyer, Stephen Connolly, and others 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Jenkins Remoting 2 | Copyright (c) 2004-2016, Sun Microsystems, Inc., Kohsuke Kawaguchi, Daniel Dyer, Stephen Connolly, and others 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | - client needs to send a break between commands 2 | -> ... which means the remoting core needs to be chunking aware, and therefore 3 | AbstractByteArrayCommandTransport can just use one ObjectOutputStream per command 4 | 5 | - server doesn't echo back CloseCommand correctly because it's using single SelectableChannel 6 | and ChannelPair.closeRead() closes the writer before stuff gets sent. 7 | Half close needs to be handled properly 8 | 9 | Phase 2: 10 | Socket connecting NioChannelHub to thread-driven Channel inside the same process 11 | 12 | Phase 3: 13 | process-based channel client connected to NioChannelHub 14 | 15 | Phase 3: 16 | client-side NioChannelHub 17 | 18 | TODOs: 19 | - multi-threaded acceptors? 20 | ======= 21 | Prefetch branch TODO before merge 22 | 23 | - constant pool analyzer must be shaded & rebundled 24 | - some classes should be traversed more deeply 25 | - base class and interfaces (this might be easy enough) 26 | - types used in static fields 27 | 28 | - ClassLoader oracles 29 | - for creating a smarter remote classloader clone 30 | built on top of ClassLoader -> IClassLoader translation service 31 | & jar transfer service 32 | 33 | - if jar is still loading, fall back to fetch2 34 | -------------------------------------------------------------------------------- /docs/close.md: -------------------------------------------------------------------------------- 1 | # Shutdown sequence of a Channel 2 | A `Channel` builds on top of two uni-directional stream of bytes that can be independently closed, like a TCP socket. 3 | 4 | ## Orderly shutdown 5 | The orderly shutdown of a channel goes through a sequence somewhat like 6 | [a termination of a TCP socket](http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_termination). 7 | 8 | The shutdown sequence starts by the `Channel.close()` call, which sends out a `CloseCommand` (equivalent of TCP FIN.) 9 | The receiver marks that, sends out its own `CloseCommand` in response. The initiator receives and executes this, 10 | successfully closing down both directions of the streams. 11 | 12 | A `CloseCommand` is always the last command on a stream. This sender uses `outClosed` field to track if it has 13 | already sent out the `CloseCommand`. When it sends out the command, the field is set to non-null. Likewise, 14 | the receiver uses `inClosed` field to track whether it has received a `CloseCommand`. 15 | 16 | 17 | ## Unorderly shutdown 18 | A termination can be also initiated by the reader side noticing that the sender no longer exists. 19 | This depends on the ability of the underlying transport to detect that, which can take long time 20 | to detect this situation (for example, it is possible to kill one end of a TCP socket in such 21 | a way that the other side will block forever.) 22 | 23 | Anyway, when the reader end determines that the sender is gone (such as EOF of a socket), it immediately 24 | gets to the `Channel.terminate()` method, which declares both directions of the streams dead, and 25 | generally behaves as if it has received a `CloseCommand`, except that it doesn't send one out since 26 | the transport is assumed to be dead. 27 | 28 | (TODO: now that I'm writing about it, I feel like it should try to send out `CloseCommand`, because 29 | one side noticing that the connection is bad doesn't mean the other side notices it right away.) -------------------------------------------------------------------------------- /docs/jenkins-specifics.md: -------------------------------------------------------------------------------- 1 | Jenkins specifics 2 | ===== 3 | 4 | This page contains information about use-cases specific to Jenkins. 5 | 6 | NOTE: The page is under construction -------------------------------------------------------------------------------- /docs/no_proxy.md: -------------------------------------------------------------------------------- 1 | # NO_PROXY Environment Variable 2 | 3 | This optional setting provides a way to specify one or more hosts where the connection should not be proxied. 4 | Similar settings are available in many applications. Unfortunately, there is no standard specification across the different implementations. 5 | Each one defines its own syntax and supported capabilities. 6 | 7 | Remoting provides an implementation that is similar to a number of other applications and systems. Its simple rules provide 8 | sufficient power and flexibility for many needs. 9 | 10 | Rules: 11 | 1. Environment variable may be named `NO_PROXY` or `no_proxy`. 12 | 1. Different specification pieces may be separated by a comma (`,`) or a pipe (`|`). 13 | 1. Each piece may specify an IP address, a network, or a host / domain. 14 | 1. An IP address may be IPv4 or IPv6. 15 | 1. An IPv6 address may be expressed in full or compressed form. 16 | 1. A network is expressed in CIDR notation, for example, `192.168.17.0/24`. 17 | 1. Localhost and loopback addresses are not proxied by default. 18 | 1. All subdomains matching a domain or host are not proxied. 19 | 1. A `NO_PROXY` setting of `jenkins.io` matches `repo.jenkins.io`, `sub.sub.jenkins.io`, and `jenkins.io`. It also matches `myjenkins.io`. 20 | 1. The following forms are identical: `jenkins.io`, `.jenkins.io`, and `*.jenkins.io`. 21 | 1. All other notations are ignored. 22 | 23 | ## History 24 | 25 | The exact implementation has changed in different remoting versions. The 3.28 release of Remoting clarified and expanded a 26 | number of these capabilities. All specifications that worked prior to 3.28 should still work. Enhancements added at 3.28 27 | include environment variable capitalization, pipe separation, compressed 28 | IPv6 form, networks, and simple hostnames or root domains. The [Changelog](../CHANGELOG.md) provides information on some of the prior changes. 29 | -------------------------------------------------------------------------------- /docs/protocol-stack.dot: -------------------------------------------------------------------------------- 1 | digraph g { 2 | rankdir=LR; 3 | node [shape=record]; 4 | null1 [label="null"]; 5 | network [label=" nextRecv| BIONetworkLayer| nextSend"]; 6 | filter1 [label=" nextRecv| AgentProtocolClientFilterLayer| nextSend"]; 7 | filter2 [label=" nextRecv| AckFilterLayer| nextSend"]; 8 | filter3 [label=" nextRecv| SSLEngineFilterLayer| nextSend"]; 9 | filter4 [label=" nextRecv| ConnectionHeadersFilterLayer| nextSend"]; 10 | application [label=" nextRecv| ChannelApplicationLayer| nextSend"]; 11 | null2 [label="null"]; 12 | network:f2 -> null1; 13 | network:f0 -> filter1:f1; 14 | filter1:f0 -> filter2:f1; 15 | filter1:f2 -> network:f1; 16 | filter2:f0 -> filter3:f1; 17 | filter2:f2 -> filter1:f1; 18 | filter3:f0 -> filter4:f1; 19 | filter3:f2 -> filter2:f1; 20 | filter4:f0 -> application:f1; 21 | filter4:f2 -> filter3:f1; 22 | application:f0 -> null2; 23 | application:f2 -> filter4:f1; 24 | } 25 | -------------------------------------------------------------------------------- /docs/protocols.md: -------------------------------------------------------------------------------- 1 | Remoting protocols 2 | ==== 3 | 4 | The Remoting library provides APIs which allow custom communication protocols to be implemented. 5 | 6 | This section describes only the protocols available within the remoting library. 7 | 8 | ## Active protocols 9 | 10 | This section lists all actively maintained protocols offered in Remoting. 11 | There may be other actively maintained protocols in other Jenkins and 3rd-party components. 12 | 13 | ### JNLP4-connect 14 | 15 | * Introduced in: Remoting 3.0, [JENKINS-36871](https://issues.jenkins-ci.org/browse/JENKINS-36871) 16 | 17 | This protocol uses the SSLEngine provided by the Java Cryptography Architecture 18 | to perform a TLS upgrade of the plaintext connection before any connection secrets are exchanged. 19 | The subsequent connection is then secured using TLS. 20 | 21 | The encryption algorithms and cyphers used by the SSLEngine when using Oracle JDK 1.8 22 | are described in [Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8](http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html)) 23 | If stronger algorithms are needed (for example, AES with 256-bit keys), the [JCE Unlimited Strength Jurisdiction Policy Files](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 24 | can be obtained on Oracle website and installed in the JDK/JRE. 25 | 26 | 27 | Protocol uses non-blocking I/O wherever possible which removes the performance bottleneck of the JNLP3-connect protocol. 28 | 29 | The protocol stack starts with the network layer, proceeds to an arbitrary number of filter layers, and ends with the application layer. 30 | It is represented as a doubly-linked list: 31 | 32 | ![Protocol Stack](protocol-stack.svg) 33 | 34 | ### WebSocket 35 | 36 | * Introduced in: Remoting version 4.0, [JEP-222](https://jenkins.io/jep/222) 37 | * At initial release, this protocol is considered a beta release and has not yet undergone extensive testing. 38 | 39 | Uses WebSocket over an HTTP(S) port to handle handshakes, encryption, framing, etc. 40 | 41 | ## Plugin protocols 42 | 43 | ### Remoting Kafka Plugin 44 | 45 | * [Remoting Kafka Plugin](https://github.com/jenkinsci/remoting-kafka-plugin) uses Kafka as fault-tolerant communication layer to support command invocation between Jenkins controller and agent. 46 | * The plugin gets rid of current direct TCP connection between controller and agent. 47 | * More info can be found in the technical [documentation](https://github.com/jenkinsci/remoting-kafka-plugin/blob/master/docs/DOCUMENTATION.md) of the plugin. 48 | -------------------------------------------------------------------------------- /docs/remoting-kafka-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/remoting/1d80543ebaf27c64e3641d6a67993a13ca9cd7f6/docs/remoting-kafka-architecture.png -------------------------------------------------------------------------------- /docs/tcpAgentStatus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/remoting/1d80543ebaf27c64e3641d6a67993a13ca9cd7f6/docs/tcpAgentStatus.png -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | Troubleshooting Remoting issues 2 | ==== 3 | 4 | This page provides information about common remoting issues and the ways allowing to triangulate and solve them. 5 | In particular cases the page provides references to external resources. 6 | 7 | NOTE: The page is under construction -------------------------------------------------------------------------------- /docs/workDir.md: -------------------------------------------------------------------------------- 1 | Remoting Work directory 2 | === 3 | 4 | In Remoting work directory is an internal data storage, which may be used by Remoting to store caches, logs and other metadata. 5 | 6 | Remoting work directory is available starting from Remoting `3.8`, which is available in [Jenkins 2.68](https://jenkins.io/changelog/#v2.68)). 7 | Before this version there was no working directory concept in the library itself; 8 | all operations were managed by library users (e.g. Jenkins agent workspaces). 9 | 10 | ### Before Remoting 3.8 (Jenkins 2.68) 11 | 12 | * There is no work directory management in Remoting itself 13 | * Logs are not being persisted to the disk unless `-agentLog` option is specified 14 | * JAR Cache is being stored in `${user.home}/.jenkins` unless `-jarCache` option is specified 15 | 16 | ### After Remoting 3.8 (Jenkins 2.68) 17 | 18 | Due to compatibility reasons, Remoting retains the legacy behavior by default. 19 | Work directory can be enabled using the `-workDir` option in CLI. 20 | 21 | Once the option is enabled, Remoting starts using the following structure: 22 | 23 | ``` 24 | ${WORKDIR} 25 | |_ ${INTERNAL_DIR} - defined by '-internalDir', 'remoting' by default 26 | |_ jarCache - JAR Cache 27 | |_ logs - Remoting logs 28 | |_ ... - Other directories contributed by library users 29 | ``` 30 | 31 | Structure of the `logs` directory depends on the logging settings. 32 | See [this page](logging.md) for more information. 33 | 34 | ### Migrating to work directories in Jenkins 35 | 36 | :exclamation: Remoting does not perform migration from the previous structure, 37 | because it cannot identify potential external users of the data. 38 | 39 | Once the `-workDir` flag is enabled in Remoting, admins are expected to do the following: 40 | 41 | 1. Remove the `${user.home}/.jenkins` directory if there is no other Remoting instances running under the same user. 42 | 2. Consider upgrading configurations of agents in order to enable Work Directories 43 | * SSH agents can be configured in agent settings. 44 | * JNLP agents should be started with the `-workDir` parameter. 45 | * See [JENKINS-44108](https://issues.jenkins-ci.org/browse/JENKINS-44108) for more information about changes in Jenkins plugins, which enable work directories by default. 46 | -------------------------------------------------------------------------------- /src/assembly/agent-load-test.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | agent-load-test 6 | 7 | tar.gz 8 | zip 9 | 10 | false 11 | 12 | 13 | /repo 14 | true 15 | true 16 | test 17 | 18 | 19 | 20 | 21 | ${project.basedir}/src/assembly/agent-load-test 22 | bin 23 | 24 | agent-load-test 25 | 26 | 0755 27 | true 28 | 29 | 30 | ${project.basedir}/src/assembly/agent-load-test 31 | bin 32 | 33 | agent-load-test.bat 34 | 35 | true 36 | 37 | 38 | ${project.basedir}/src/assembly/agent-load-test 39 | ./ 40 | 41 | agent-load-test* 42 | 43 | true 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/assembly/agent-load-test/NOTICE: -------------------------------------------------------------------------------- 1 | Jenkins Remoting 2 | Copyright (c) 2004-2016, Sun Microsystems, Inc., Kohsuke Kawaguchi, Daniel Dyer, Stephen Connolly, and others 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | Apache Ant 8 | Copyright 1999-2012 The Apache Software Foundation 9 | 10 | The task is based on code Copyright (c) 2002, Landmark 11 | Graphics Corp that has been kindly donated to the Apache Software 12 | Foundation. 13 | 14 | Apache Commons IO 15 | Copyright 2002-2012 The Apache Software Foundation 16 | 17 | Objenesis 18 | Copyright 2006-2013 Joe Walnes, Henri Tremblay, Leonardo Mesquita 19 | 20 | Mockito license - MIT. 21 | 22 | Cglib - Apache License 2.0 23 | ASM - BSD license 24 | 25 | Mockito all distribution: 26 | 27 | Objenesis - MIT license 28 | Hamcrest - BSD license 29 | -------------------------------------------------------------------------------- /src/assembly/agent-load-test/README.md: -------------------------------------------------------------------------------- 1 | Jenkins Remoting Agent Load Tester 2 | ================================== 3 | 4 | This is a test utility to determine the capacity limits of your Jenkins controller. 5 | Using this utility, you can fire up a remoting server that accepts a number of 6 | loopback connections in order to see what load the JNLP remoting protocols 7 | produce on your server. 8 | 9 | To get a comparable set of measures you can run the following commands: 10 | 11 | * Unix systems 12 | 13 | bin/agent-load-test --protocol JNLP2-connect --warmup 60 --collect 60 --stats stats.csv 14 | bin/agent-load-test --protocol JNLP2-connect --warmup 60 --collect 60 --stats stats.csv --bio 15 | bin/agent-load-test --protocol JNLP3-connect --warmup 60 --collect 60 --stats stats.csv --bio 16 | bin/agent-load-test --protocol JNLP4-connect --warmup 60 --collect 60 --stats stats.csv 17 | bin/agent-load-test --protocol JNLP4-connect --warmup 60 --collect 60 --stats stats.csv --bio 18 | bin/agent-load-test --protocol JNLP4-plaintext --warmup 60 --collect 60 --stats stats.csv 19 | bin/agent-load-test --protocol JNLP4-plaintext --warmup 60 --collect 60 --stats stats.csv --bio 20 | 21 | * Windows systems 22 | 23 | bin\agent-load-test --protocol JNLP2-connect --warmup 60 --collect 60 --stats stats.csv 24 | bin\agent-load-test --protocol JNLP2-connect --warmup 60 --collect 60 --stats stats.csv --bio 25 | bin\agent-load-test --protocol JNLP3-connect --warmup 60 --collect 60 --stats stats.csv --bio 26 | bin\agent-load-test --protocol JNLP4-connect --warmup 60 --collect 60 --stats stats.csv 27 | bin\agent-load-test --protocol JNLP4-connect --warmup 60 --collect 60 --stats stats.csv --bio 28 | bin\agent-load-test --protocol JNLP4-plaintext --warmup 60 --collect 60 --stats stats.csv 29 | bin\agent-load-test --protocol JNLP4-plaintext --warmup 60 --collect 60 --stats stats.csv --bio 30 | -------------------------------------------------------------------------------- /src/filter/resources/hudson/remoting/hudson-version.properties: -------------------------------------------------------------------------------- 1 | version=${build.version} -------------------------------------------------------------------------------- /src/filter/resources/jenkins/remoting/jenkins-version.properties: -------------------------------------------------------------------------------- 1 | version=${build.version} -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/AbstractSynchronousByteArrayCommandTransport.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.ObjectOutputStream; 6 | import org.jenkinsci.remoting.util.AnonymousClassWarnings; 7 | 8 | /** 9 | * {@link SynchronousCommandTransport} that works with {@code byte[]} instead of command object. 10 | * 11 | * This base class hides away some of the {@link Command} serialization details. One less thing 12 | * for transport implementers to worry about. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | * @since 2.13 16 | */ 17 | public abstract class AbstractSynchronousByteArrayCommandTransport extends SynchronousCommandTransport { 18 | /** 19 | * Read a byte[] from the underlying transport for the given channel. 20 | */ 21 | public abstract byte[] readBlock(Channel channel) throws IOException, ClassNotFoundException; 22 | 23 | /** 24 | * Writes a byte[] to the transport. 25 | * 26 | * The block boundary is significant. A transport needs to ensure that that the same byte[] is 27 | * read by the peer through {@link #readBlock(Channel)} (unlike TCP, where a single write can 28 | * be split into multiple read()s on the other side.) 29 | */ 30 | public abstract void writeBlock(Channel channel, byte[] payload) throws IOException; 31 | 32 | @Override 33 | public Command read() throws IOException, ClassNotFoundException { 34 | byte[] block = readBlock(channel); 35 | return Command.readFrom(channel, block); 36 | } 37 | 38 | @Override 39 | public void write(Command cmd, boolean last) throws IOException { 40 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 41 | try (ObjectOutputStream oos = AnonymousClassWarnings.checkingObjectOutputStream(baos)) { 42 | cmd.writeTo(channel, oos); 43 | } 44 | byte[] block = baos.toByteArray(); 45 | channel.notifyWrite(cmd, block.length); 46 | writeBlock(channel, block); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/Asynchronous.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Used on a method in a remotable exported interface to designate 11 | * that the call is made asynchronously. The call will be issued, 12 | * but the caller will return without waiting for the return value 13 | * to come back from the other side. 14 | * 15 | * The signature of the method must return void. 16 | * 17 | *
18 |  * interface Foo {
19 |  *     void bar();
20 |  *     @Asynchronous
21 |  *     void zot();
22 |  * }
23 |  *
24 |  * Foo foo = getSomeRemoteReferenceToFoo();
25 |  * // this invocation calls a remote method, wait for that to complete,
26 |  * // then return.
27 |  * foo.bar();
28 |  * // this invocation returns immediately after the request to execute a remote method
29 |  * // is sent to the other side. There's no ordering guarantee as to when
30 |  * // this method actually gets executed. For example, if you invoke two async
31 |  * // calls, they may execute in the reverse order.
32 |  * foo.zot();
33 |  * 
34 | * 35 | * @see Channel#callAsync(Callable) 36 | * @author Kohsuke Kawaguchi 37 | * @since 2.24 38 | */ 39 | @Documented 40 | @Target(ElementType.METHOD) 41 | @Retention(RetentionPolicy.RUNTIME) 42 | public @interface Asynchronous {} 43 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/CallableDecoratorAdapter.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import org.jenkinsci.remoting.CallableDecorator; 4 | 5 | /** 6 | * Use {@link CallableFilter} as {@link CallableDecorator} 7 | * @author Kohsuke Kawaguchi 8 | */ 9 | class CallableDecoratorAdapter extends CallableDecorator { 10 | private final CallableFilter filter; 11 | 12 | public CallableDecoratorAdapter(CallableFilter filter) { 13 | this.filter = filter; 14 | } 15 | 16 | @Override 17 | public V call(java.util.concurrent.Callable callable) throws Exception { 18 | return filter.call(callable); 19 | } 20 | 21 | @Override 22 | public int hashCode() { 23 | return filter.hashCode(); 24 | } 25 | 26 | @Override 27 | public boolean equals(Object obj) { 28 | if (obj != null && obj.getClass() == this.getClass()) { 29 | return ((CallableDecoratorAdapter) obj).filter.equals(filter); 30 | } 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/CallableDecoratorList.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.util.concurrent.CopyOnWriteArrayList; 4 | import org.jenkinsci.remoting.CallableDecorator; 5 | 6 | /** 7 | * List of {@link CallableDecorator} that provides aggregated decoration operation. 8 | * 9 | * @author Kohsuke Kawaguchi 10 | */ 11 | class CallableDecoratorList extends CopyOnWriteArrayList { 12 | java.util.concurrent.Callable wrapCallable(java.util.concurrent.Callable r) { 13 | for (CallableDecorator d : this) { 14 | r = applyDecorator(r, d); 15 | } 16 | return r; 17 | } 18 | 19 | private java.util.concurrent.Callable applyDecorator( 20 | final java.util.concurrent.Callable inner, final CallableDecorator filter) { 21 | return () -> filter.call(inner); 22 | } 23 | 24 | Callable wrapUserRequest(final Callable c) { 25 | Callable decorated = c; 26 | 27 | for (CallableDecorator d : this) { 28 | decorated = d.userRequest(c, decorated); 29 | } 30 | 31 | return decorated; 32 | } 33 | 34 | // this class isn't actually getting serialized, but this makes FindBugs happy 35 | private static final long serialVersionUID = 1L; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/CallableFilter.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import org.jenkinsci.remoting.CallableDecorator; 4 | 5 | /** 6 | * Decorator on {@code Callable.call()} to filter the execution. 7 | * 8 | * @author Kohsuke Kawaguchi 9 | * @deprecated 10 | * Use {@link CallableDecorator} 11 | */ 12 | @Deprecated 13 | public interface CallableFilter { 14 | /** 15 | * This implementation should normally look something like this: 16 | * 17 | *
18 |      * V call(Callable c) {
19 |      *     doSomePrep();
20 |      *     try {
21 |      *         return c.call();
22 |      *     } finally {
23 |      *         doSomeCleanUp();
24 |      *     }
25 |      * }
26 |      * 
27 | */ 28 | V call(java.util.concurrent.Callable callable) throws Exception; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ChannelClosedException.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.CheckForNull; 4 | import edu.umd.cs.findbugs.annotations.NonNull; 5 | import org.jenkinsci.remoting.ChannelStateException; 6 | 7 | /** 8 | * Indicates that the channel is already closed or being closed. 9 | * 10 | * @author Kohsuke Kawaguchi 11 | */ 12 | public class ChannelClosedException extends ChannelStateException { 13 | /** 14 | * @deprecated 15 | * Use {@link #ChannelClosedException(Throwable)} or {@link #ChannelClosedException(String, Throwable)}. 16 | * This constructor will not include cause of the termination. 17 | */ 18 | @Deprecated 19 | public ChannelClosedException() { 20 | this(null, "channel is already closed", null); 21 | } 22 | 23 | /** 24 | * @deprecated Use {@link #ChannelClosedException(Channel, Throwable)} 25 | */ 26 | @Deprecated 27 | public ChannelClosedException(Throwable cause) { 28 | this((Channel) null, cause); 29 | } 30 | 31 | /** 32 | * Constructor. 33 | * @param channel Reference to the channel. {@code null} if the channel is unknown. 34 | * @param cause Cause 35 | * @since 3.15 36 | */ 37 | public ChannelClosedException(@CheckForNull Channel channel, @CheckForNull Throwable cause) { 38 | super(channel, "channel is already closed", cause); 39 | } 40 | 41 | /** 42 | * Constructor. 43 | * 44 | * @param message Message 45 | * @param cause Cause of the channel close/termination. 46 | * May be {@code null} if it cannot be determined when the exception is constructed. 47 | * @since 3.11 48 | * @deprecated Use {@link #ChannelClosedException(Channel, String, Throwable)} 49 | */ 50 | @Deprecated 51 | public ChannelClosedException(@NonNull String message, @CheckForNull Throwable cause) { 52 | this(null, message, cause); 53 | } 54 | 55 | /** 56 | * Constructor. 57 | * 58 | * @param channel Reference to the channel. {@code null} if the channel is unknown. 59 | * @param message Message 60 | * @param cause Cause of the channel close/termination. 61 | * May be {@code null} if it cannot be determined when the exception is constructed. 62 | * @since 3.15 63 | */ 64 | public ChannelClosedException( 65 | @CheckForNull Channel channel, @NonNull String message, @CheckForNull Throwable cause) { 66 | super(channel, message, cause); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ChannelProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2010, InfraDNA, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package hudson.remoting; 26 | 27 | import java.io.Serializable; 28 | 29 | /** 30 | * A convenient key type for {@link Channel#getProperty(Object)} and {@link Channel#setProperty(Object, Object)} 31 | * 32 | * @author Kohsuke Kawaguchi 33 | */ 34 | public class ChannelProperty implements Serializable { 35 | public final Class type; 36 | public final String displayName; 37 | 38 | public ChannelProperty(Class type, String displayName) { 39 | this.type = type; 40 | this.displayName = displayName; 41 | } 42 | 43 | private static final long serialVersionUID = 1L; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ChunkHeader.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.nio.ByteBuffer; 4 | import org.jenkinsci.remoting.util.ByteBufferQueue; 5 | 6 | /** 7 | * Parsing of the chunk header. 8 | * 9 | *

10 | * The header is {@link #SIZE} bytes, in the network order. The first bit designates whether this chunk 11 | * is the last chunk (0 if this is the last chunk), and the remaining 15 bits designate the 12 | * length of the chunk as unsigned number. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | */ 16 | public class ChunkHeader { 17 | 18 | public static final int SIZE = 2; 19 | 20 | public static int read(ByteBuffer buf) { 21 | return parse(buf.get(), buf.get()); 22 | } 23 | 24 | public static int peek(ByteBuffer buf) { 25 | return peek(buf, 0); 26 | } 27 | 28 | public static int peek(ByteBuffer buf, int pos) { 29 | return parse(buf.get(pos), buf.get(pos + 1)); 30 | } 31 | 32 | public static int parse(byte[] buf) { 33 | return parse(buf, 0); 34 | } 35 | 36 | public static int parse(byte[] buf, int pos) { 37 | return parse(buf[pos], buf[pos + 1]); 38 | } 39 | 40 | public static int parse(int b1, int b2) { 41 | return ((b1 & 0xFF) << 8) | (b2 & 0xFF); 42 | } 43 | 44 | public static boolean isLast(int header) { 45 | return (header & 0x8000) == 0; 46 | } 47 | 48 | public static int length(int header) { 49 | return header & 0x7FFF; 50 | } 51 | 52 | public static void write(ByteBuffer buf, int length, boolean hasMore) { 53 | buf.put((byte) ((hasMore ? 0x80 : 0) | (length >> 8))); 54 | buf.put((byte) length); 55 | } 56 | 57 | public static void write(ByteBufferQueue buf, int length, boolean hasMore) { 58 | buf.put((byte) ((hasMore ? 0x80 : 0) | (length >> 8))); 59 | buf.put((byte) length); 60 | } 61 | 62 | public static byte[] pack(int length, boolean hasMore) { 63 | byte[] header = new byte[SIZE]; 64 | header[0] = (byte) ((hasMore ? 0x80 : 0) | (length >> 8)); 65 | header[1] = (byte) (length); 66 | return header; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ChunkedCommandTransport.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | /** 9 | * A variation of {@link ClassicCommandTransport} that uses the chunked encoding. 10 | * 11 | * @author Kohsuke Kawaguchi 12 | */ 13 | class ChunkedCommandTransport extends AbstractSynchronousByteArrayCommandTransport { 14 | private final Capability remoteCapability; 15 | 16 | private final ChunkedInputStream in; 17 | private final ChunkedOutputStream out; 18 | 19 | /** 20 | * See {@link CommandTransport#getUnderlyingStream()} 21 | */ 22 | private final OutputStream rawOut; 23 | 24 | /*package*/ ChunkedCommandTransport( 25 | Capability remoteCapability, InputStream in, OutputStream out, OutputStream rawOut) { 26 | this.remoteCapability = remoteCapability; 27 | this.in = new ChunkedInputStream(in); 28 | this.out = new ChunkedOutputStream(8192, out); 29 | this.rawOut = rawOut; 30 | } 31 | 32 | @Override 33 | public Capability getRemoteCapability() throws IOException { 34 | return remoteCapability; 35 | } 36 | 37 | @Override 38 | public byte[] readBlock(Channel channel) throws IOException, ClassNotFoundException { 39 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 40 | in.readUntilBreak(baos); 41 | return baos.toByteArray(); 42 | } 43 | 44 | @Override 45 | public void writeBlock(Channel channel, byte[] payload) throws IOException { 46 | out.write(payload); 47 | out.sendBreak(); 48 | } 49 | 50 | @Override 51 | public void closeWrite() throws IOException { 52 | out.close(); 53 | } 54 | 55 | @Override 56 | public void closeRead() throws IOException { 57 | in.close(); 58 | } 59 | 60 | @Override 61 | OutputStream getUnderlyingStream() { 62 | return rawOut; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ClassLoaderHolder.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.CheckForNull; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | import org.jenkinsci.remoting.SerializableOnlyOverRemoting; 9 | 10 | /** 11 | * Remoting-aware holder of {@link ClassLoader} that replaces ClassLoader by its {@link RemoteClassLoader}. 12 | * 13 | * @author Kohsuke Kawaguchi 14 | * @since 2.12 15 | */ 16 | public class ClassLoaderHolder implements SerializableOnlyOverRemoting { 17 | 18 | @CheckForNull 19 | private transient ClassLoader classLoader; 20 | 21 | public ClassLoaderHolder(@CheckForNull ClassLoader classLoader) { 22 | this.classLoader = classLoader; 23 | } 24 | 25 | public ClassLoaderHolder() {} 26 | 27 | @CheckForNull 28 | public ClassLoader get() { 29 | return classLoader; 30 | } 31 | 32 | public void set(@CheckForNull ClassLoader classLoader) { 33 | this.classLoader = classLoader; 34 | } 35 | 36 | @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_READ_OBJECT", justification = "TODO needs triage") 37 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 38 | RemoteClassLoader.IClassLoader proxy = (RemoteClassLoader.IClassLoader) ois.readObject(); 39 | classLoader = proxy == null 40 | ? null 41 | : getChannelForSerialization().importedClassLoaders.get(proxy); 42 | } 43 | 44 | @SuppressFBWarnings( 45 | value = "DMI_NONSERIALIZABLE_OBJECT_WRITTEN", 46 | justification = "RemoteClassLoader.export() produces a serializable wrapper class") 47 | private void writeObject(ObjectOutputStream oos) throws IOException { 48 | if (classLoader == null) { 49 | oos.writeObject(null); 50 | } else { 51 | RemoteClassLoader.IClassLoader proxy = RemoteClassLoader.export(classLoader, getChannelForSerialization()); 52 | oos.writeObject(proxy); 53 | } 54 | } 55 | 56 | private static final long serialVersionUID = 1L; 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/DaemonThreadFactory.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import java.util.concurrent.ThreadFactory; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | /** 9 | * @author Kohsuke Kawaguchi 10 | */ 11 | public class DaemonThreadFactory implements ThreadFactory { 12 | private static final Logger LOGGER = Logger.getLogger(DaemonThreadFactory.class.getName()); 13 | 14 | @Override 15 | public Thread newThread(@NonNull Runnable r) { 16 | Thread thread = new Thread(r); 17 | thread.setDaemon(true); 18 | thread.setUncaughtExceptionHandler( 19 | (t, e) -> LOGGER.log(Level.SEVERE, e, () -> "Unhandled exception in thread " + t)); 20 | return thread; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/DiagnosedStreamCorruptionException.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | import java.io.PrintWriter; 6 | import java.io.StreamCorruptedException; 7 | import java.io.StringWriter; 8 | 9 | /** 10 | * Signals a {@link StreamCorruptedException} with some additional diagnostic information. 11 | * 12 | * @author Kohsuke Kawaguchi 13 | */ 14 | public class DiagnosedStreamCorruptionException extends StreamCorruptedException { 15 | private final Exception diagnoseFailure; 16 | 17 | @NonNull 18 | private final byte[] readBack; 19 | 20 | @NonNull 21 | private final byte[] readAhead; 22 | 23 | DiagnosedStreamCorruptionException( 24 | Exception cause, Exception diagnoseFailure, @NonNull byte[] readBack, @NonNull byte[] readAhead) { 25 | initCause(cause); 26 | this.diagnoseFailure = diagnoseFailure; 27 | this.readBack = readBack; 28 | this.readAhead = readAhead; 29 | } 30 | 31 | public Exception getDiagnoseFailure() { 32 | return diagnoseFailure; 33 | } 34 | 35 | @NonNull 36 | public byte[] getReadBack() { 37 | return readBack.clone(); 38 | } 39 | 40 | @NonNull 41 | public byte[] getReadAhead() { 42 | return readAhead.clone(); 43 | } 44 | 45 | @Override 46 | @SuppressFBWarnings( 47 | value = "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE", 48 | justification = "Used for diagnosing stream corruption between agent and server.") 49 | public String toString() { 50 | StringBuilder buf = new StringBuilder(); 51 | buf.append(super.toString()).append("\n"); 52 | buf.append("Read back: ").append(HexDump.toHex(readBack)).append('\n'); 53 | buf.append("Read ahead: ").append(HexDump.toHex(readAhead)); 54 | if (diagnoseFailure != null) { 55 | StringWriter w = new StringWriter(); 56 | PrintWriter p = new PrintWriter(w); 57 | diagnoseFailure.printStackTrace(p); 58 | p.flush(); 59 | 60 | buf.append("\nDiagnosis problem:\n "); 61 | buf.append(w.toString().trim().replace("\n", "\n ")); 62 | } 63 | return buf.toString(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/EngineListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import javax.swing.SwingUtilities; 27 | 28 | /** 29 | * Receives status notification from {@link Engine}. 30 | * 31 | *

32 | * The callback will be invoked on a non-GUI thread, so if the implementation 33 | * wants to touch Swing, {@link SwingUtilities#invokeLater(Runnable)} would be needed. 34 | * 35 | *

36 | * To implement this interface outside this module, extend from {@link EngineListenerAdapter} 37 | * instead to protect against method additions in the future. 38 | * 39 | * @author Kohsuke Kawaguchi 40 | */ 41 | public interface EngineListener { 42 | /** 43 | * Status message that indicates the progress of the operation. 44 | */ 45 | void status(String msg); 46 | 47 | /** 48 | * Status message, with additional stack trace that indicates an error that was recovered. 49 | */ 50 | void status(String msg, Throwable t); 51 | 52 | /** 53 | * Fatal error that's non recoverable. 54 | */ 55 | void error(Throwable t); 56 | 57 | /** 58 | * Called when a connection is terminated. 59 | */ 60 | void onDisconnect(); 61 | 62 | /** 63 | * Called when a re-connection is about to be attempted. 64 | * @since 2.0 65 | */ 66 | void onReconnect(); 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/EngineListenerAdapter.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | /** 4 | * Adapter class for {@link EngineListener} to shield subtypes from future callback additions. 5 | * 6 | * @author Kohsuke Kawaguchi 7 | * @since 2.36 8 | */ 9 | public abstract class EngineListenerAdapter implements EngineListener { 10 | @Override 11 | public void status(String msg) {} 12 | 13 | @Override 14 | public void status(String msg, Throwable t) {} 15 | 16 | @Override 17 | public void error(Throwable t) {} 18 | 19 | @Override 20 | public void onDisconnect() {} 21 | 22 | @Override 23 | public void onReconnect() {} 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/EngineListenerSplitter.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.CopyOnWriteArrayList; 5 | 6 | /** 7 | * {@link EngineListener} that distributes callbacks. 8 | * 9 | * @author Kohsuke Kawaguchi 10 | * @since 2.36 11 | */ 12 | public class EngineListenerSplitter implements EngineListener { 13 | protected final List listeners = new CopyOnWriteArrayList<>(); 14 | 15 | public void add(EngineListener el) { 16 | listeners.add(el); 17 | } 18 | 19 | public void remove(EngineListener el) { 20 | listeners.remove(el); 21 | } 22 | 23 | @Override 24 | public void status(String msg) { 25 | for (EngineListener l : listeners) { 26 | l.status(msg); 27 | } 28 | } 29 | 30 | @Override 31 | public void status(String msg, Throwable t) { 32 | for (EngineListener l : listeners) { 33 | l.status(msg, t); 34 | } 35 | } 36 | 37 | @Override 38 | public void error(Throwable t) { 39 | for (EngineListener l : listeners) { 40 | l.error(t); 41 | } 42 | } 43 | 44 | @Override 45 | public void onDisconnect() { 46 | for (EngineListener l : listeners) { 47 | l.onDisconnect(); 48 | } 49 | } 50 | 51 | @Override 52 | public void onReconnect() { 53 | for (EngineListener l : listeners) { 54 | l.onReconnect(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ErrorPropagatingOutputStream.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.CheckForNull; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | /** 9 | * {@link OutputStream} that's connected to an {@link InputStream} somewhere, 10 | * which provides ability to have {@link InputStream} report an error. 11 | * 12 | * @author Kohsuke Kawaguchi 13 | * @since 2.35 14 | */ 15 | public interface ErrorPropagatingOutputStream { 16 | /** 17 | * Closes the stream and causes the reading {@link InputStream} to report an error. 18 | * 19 | *

20 | * This method is somewhat like {@link OutputStream#close()}, 21 | * in that it signals the end of a stream. In addition to what the close method does, 22 | * this method will cause the {@link InputStream#read()} 23 | * method (or any other overloaded versions) to throw an 24 | * {@link IOException} with the given throwable as the cause. 25 | * 26 | *

27 | * {@link InputStream} will report an error only after all the data that has written 28 | * before is read. IOW, the error will not magically jump over the data that was written. 29 | * 30 | *

31 | * This is useful to propagate error over a pipe. If used over 32 | * a channel with the remoting library that doesn't yet support this, 33 | * or if the {@link OutputStream} isn't connecting to an {@link InputStream}, 34 | * this method behaves exactly like {@link OutputStream#close()}. 35 | * 36 | *

37 | * If {@link OutputStream} is already closed or error state is 38 | * set, this method will be no-op. 39 | * 40 | * @param e 41 | * if null, this method behaves exactly like {@link OutputStream#close()} 42 | */ 43 | void error(@CheckForNull Throwable e) throws IOException; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/Future.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 27 | 28 | // TODO: This Future should be actually deprecated, because it may confuse API users 29 | /** 30 | * Alias to {@link Future}. 31 | * 32 | *

33 | * This alias is defined so that retro-translation won't affect 34 | * the publicly committed signature of the API. 35 | * 36 | * @author Kohsuke Kawaguchi 37 | */ 38 | @SuppressFBWarnings( 39 | value = "NM_SAME_SIMPLE_NAME_AS_INTERFACE", 40 | justification = "This class is just an alias, but this alias is a part of public API") 41 | public interface Future extends java.util.concurrent.Future {} 42 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/FutureAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import edu.umd.cs.findbugs.annotations.NonNull; 27 | import java.util.concurrent.ExecutionException; 28 | import java.util.concurrent.TimeUnit; 29 | import java.util.concurrent.TimeoutException; 30 | 31 | /** 32 | * {@link Future} that converts the return type. 33 | * 34 | * @author Kohsuke Kawaguchi 35 | */ 36 | abstract class FutureAdapter implements Future { 37 | protected final Future core; 38 | 39 | protected FutureAdapter(Future core) { 40 | this.core = core; 41 | } 42 | 43 | @Override 44 | public boolean cancel(boolean mayInterruptIfRunning) { 45 | return core.cancel(mayInterruptIfRunning); 46 | } 47 | 48 | @Override 49 | public boolean isCancelled() { 50 | return core.isCancelled(); 51 | } 52 | 53 | @Override 54 | public boolean isDone() { 55 | return core.isDone(); 56 | } 57 | 58 | @Override 59 | public X get() throws InterruptedException, ExecutionException { 60 | return adapt(core.get()); 61 | } 62 | 63 | @Override 64 | public X get(long timeout, @NonNull TimeUnit unit) 65 | throws InterruptedException, ExecutionException, TimeoutException { 66 | return adapt(core.get(timeout, unit)); 67 | } 68 | 69 | protected abstract X adapt(Y y) throws ExecutionException; 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/GCCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 27 | 28 | /** 29 | * Performs GC. 30 | * 31 | * @author Kohsuke Kawaguchi 32 | */ 33 | class GCCommand extends Command { 34 | @Override 35 | @SuppressFBWarnings(value = "DM_GC", justification = "TODO needs triage") 36 | protected void execute(Channel channel) { 37 | System.gc(); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "GC"; 43 | } 44 | 45 | private static final long serialVersionUID = 1L; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/HexDump.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | /** 4 | * @author Kohsuke Kawaguchi 5 | */ 6 | public class HexDump { 7 | private static final String CODE = "0123456789abcdef"; 8 | 9 | public static String toHex(byte[] buf) { 10 | return toHex(buf, 0, buf.length); 11 | } 12 | 13 | public static String toHex(byte[] buf, int start, int len) { 14 | StringBuilder r = new StringBuilder(len * 2); 15 | boolean inText = false; 16 | for (int i = 0; i < len; i++) { 17 | byte b = buf[start + i]; 18 | if (b >= 0x20 && b <= 0x7e) { 19 | if (!inText) { 20 | inText = true; 21 | r.append('\''); 22 | } 23 | r.append((char) b); 24 | } else { 25 | if (inText) { 26 | r.append("' "); 27 | inText = false; 28 | } 29 | r.append("0x"); 30 | r.append(CODE.charAt((b >> 4) & 15)); 31 | r.append(CODE.charAt(b & 15)); 32 | if (i < len - 1) { 33 | if (b == 10) { 34 | r.append('\n'); 35 | } else { 36 | r.append(' '); 37 | } 38 | } 39 | } 40 | } 41 | if (inText) { 42 | r.append('\''); 43 | } 44 | return r.toString(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/IChannel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | /** 27 | * Internally used to mark methods on {@link Channel} that are exported to remote. 28 | * 29 | *

30 | * Behaviors of the methods are explained in {@link Channel}. 31 | * 32 | * @author Kohsuke Kawaguchi 33 | */ 34 | interface IChannel { 35 | Object getProperty(Object key); 36 | 37 | Object waitForProperty(Object key) throws InterruptedException; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/IReadResolve.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import java.io.ObjectStreamException; 27 | 28 | /** 29 | * Used internally in the remoting code to have the proxy object 30 | * implement readResolve. 31 | * 32 | * @author Kohsuke Kawaguchi 33 | */ 34 | public interface IReadResolve { 35 | Object readResolve() throws ObjectStreamException; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/InitializeJarCacheMain.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import java.io.File; 5 | import java.io.FilenameFilter; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.StandardCopyOption; 9 | 10 | /** 11 | * Takes a directory of jars and populates them into the given jar cache 12 | * directory with the correct hash names. 13 | * 14 | *

Ideally this class should exist outside hudson.remoting but unfortunately 15 | * it needs access to package-private methods in hudson.remoting. 16 | * 17 | * @author Akshay Dayal 18 | */ 19 | public class InitializeJarCacheMain { 20 | 21 | private static final FilenameFilter JAR_FILE_FILTER = (dir, name) -> name.endsWith(".jar"); 22 | 23 | /** 24 | * Requires 2 parameters: 25 | *

    26 | *
  1. The source jar directory. 27 | *
  2. The jar cache directory. 28 | *
29 | */ 30 | @SuppressFBWarnings( 31 | value = "PATH_TRAVERSAL_IN", 32 | justification = 33 | "These file values are provided by users with sufficient administrative permissions to run this utility program.") 34 | public static void main(String[] argv) throws Exception { 35 | if (argv.length != 2) { 36 | throw new IllegalArgumentException("Usage: java -cp agent.jar hudson.remoting.InitializeJarCacheMain " 37 | + " "); 38 | } 39 | 40 | File sourceJarDir = new File(argv[0]); 41 | File jarCacheDir = new File(argv[1]); 42 | FileSystemJarCache jarCache = new FileSystemJarCache(jarCacheDir, false); 43 | 44 | File[] jars = sourceJarDir.listFiles(JAR_FILE_FILTER); 45 | if (jars == null) { 46 | throw new IOException("Cannot list JAR files in " + sourceJarDir); 47 | } 48 | for (File jar : jars) { 49 | Checksum checksum = Checksum.forFile(jar); 50 | File newJarLocation = jarCache.map(checksum.sum1, checksum.sum2); 51 | 52 | Files.createDirectories(newJarLocation.getParentFile().toPath()); 53 | Files.copy(jar.toPath(), newJarLocation.toPath(), StandardCopyOption.REPLACE_EXISTING); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/InternalCallable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2021, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import org.jenkinsci.remoting.RoleChecker; 27 | import org.kohsuke.accmod.Restricted; 28 | import org.kohsuke.accmod.restrictions.NoExternalUse; 29 | 30 | /** 31 | * For remoting internal use only: {@link Callable}s implement 32 | * this marker interface to be allowed to bypass the required role check of 33 | * {@link RequiredRoleCheckerWrapper}, as the application-defined 34 | * roles are unknown to remoting. 35 | * Callables defined in Jenkins need to extend {@code MasterToSlaveCallable} or 36 | * (rarely) {@code SlaveToMasterCallable}, or (almost never) implement 37 | * their own role checks. 38 | * 39 | * @since TODO 40 | */ 41 | @Restricted(NoExternalUse.class) 42 | /* package */ interface InternalCallable extends Callable { 43 | @Override 44 | default void checkRoles(RoleChecker checker) throws SecurityException { 45 | // no-op 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/JarCache.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.net.URL; 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | /** 10 | * Jar file cache. 11 | * 12 | *

13 | * The remoting library supports local jar file caching for the efficiency in remote class loading. 14 | * The cache stores jar files sent by the other side, and identifies jars with MD5 checksums that uniquely 15 | * identifies its content. 16 | * 17 | * This allows the cache to be reused by future channel sessions or other concurrent channel sessions. 18 | * 19 | * @author Kohsuke Kawaguchi 20 | * @since 2.24 21 | */ 22 | public abstract class JarCache { 23 | 24 | /** 25 | * Default JAR cache location for disabled workspace Manager. 26 | */ 27 | public static final File DEFAULT_NOWS_JAR_CACHE_LOCATION = 28 | new File(System.getProperty("user.home"), ".jenkins/cache/jars"); 29 | 30 | // TODO: replace by checked exception 31 | /** 32 | * Gets a default value for {@link FileSystemJarCache} to be initialized on agents. 33 | * @return Created JAR Cache 34 | * @throws IOException Default JAR Cache location cannot be initialized 35 | */ 36 | @NonNull 37 | /*package*/ static JarCache getDefault() throws IOException { 38 | try { 39 | return new FileSystemJarCache(DEFAULT_NOWS_JAR_CACHE_LOCATION, true); 40 | } catch (IllegalArgumentException ex) { 41 | throw new IOException("Failed to initialize the default JAR Cache location", ex); 42 | } 43 | } 44 | 45 | /** 46 | * Looks up the jar in cache, and if not found, use {@link JarLoader} to retrieve it 47 | * from the other side. 48 | * 49 | *

50 | * This method must be concurrency-safe. 51 | * 52 | * @param channel 53 | * Channel that needs this jar file. 54 | * @return 55 | * URL of the jar file. 56 | */ 57 | @NonNull 58 | public abstract CompletableFuture resolve(@NonNull Channel channel, long sum1, long sum2) 59 | throws IOException, InterruptedException; 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/JarLoader.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * Remoting interface to allow the other side to retrieve a jar file 8 | * from the checksum advertised in {@link ResourceImageInJar}. 9 | * 10 | *

11 | * {@link Channel} exposes this as {@linkplain Channel#getRemoteProperty(Object) a remote property} 12 | * under the key {@link #OURS}, then once retrieved we store it in a local property under {@link #THEIRS}. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | * @since 2.24 16 | */ 17 | public interface JarLoader { 18 | /** 19 | * Retrieve the jar file image. 20 | * 21 | * This method is called by the other side to receive the jar file. This call implicitly 22 | * has the effect of {@link #notifyJarPresence(long, long)} 23 | * 24 | * @param sink 25 | * This stream receives the jar file. 26 | * 27 | * @throws InterruptedException 28 | * Since this is a remote call, if the calling thread gets interrupted while waiting for the completion 29 | * of the call, this exception will be thrown. 30 | */ 31 | void writeJarTo(long sum1, long sum2, OutputStream sink) throws IOException, InterruptedException; 32 | 33 | /** 34 | * Called by the other side to notify that they already own the jar file of the given checksum. 35 | * 36 | * This allows this side to send {@link ResourceImageRef} smartly by avoiding unnecessary 37 | * image transport. 38 | */ 39 | @Asynchronous 40 | void notifyJarPresence(long sum1, long sum2); 41 | 42 | /** 43 | * @param sums 44 | * Array of even length. sums[2i] and sumes[2i+1] are paired up and interpreted as one checksum. 45 | */ 46 | @Asynchronous 47 | void notifyJarPresence(long[] sums); 48 | 49 | /** 50 | * Used by the local side to see if the jar file of the given checksum is already present 51 | * on the other side. Used to decide if the class file image gets sent to the remote or not. 52 | */ 53 | boolean isPresentOnRemote(Checksum sum); 54 | 55 | String OURS = JarLoader.class.getName() + ".ours"; 56 | ChannelProperty THEIRS = new ChannelProperty<>(JarLoader.class, "their JarLoader"); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/JarURLValidator.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.IOException; 4 | import java.net.URL; 5 | 6 | /** 7 | * Validate a URL attempted to be read by the remote end (agent side). 8 | * 9 | * @deprecated Do not use, intended as a temporary workaround only. 10 | */ 11 | // TODO Remove once we no longer require compatibility with remoting before 2024-08. 12 | @Deprecated 13 | public interface JarURLValidator { 14 | void validate(URL url) throws IOException; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/MimicException.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import edu.umd.cs.findbugs.annotations.Nullable; 5 | import org.kohsuke.accmod.Restricted; 6 | import org.kohsuke.accmod.restrictions.DoNotUse; 7 | 8 | /** 9 | * Exception that prints like the specified exception. 10 | * 11 | * This is used to carry the diagnostic information to the other side of the channel 12 | * in situations where we cannot use class remoting. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | * @see Capability#hasMimicException() 16 | * @deprecated Use {@link ProxyException} instead. 17 | */ 18 | @Deprecated 19 | @Restricted(DoNotUse.class) 20 | class MimicException extends Exception { 21 | private final String className; 22 | 23 | MimicException(Throwable cause) { 24 | super(cause.getMessage()); 25 | className = cause.getClass().getName(); 26 | setStackTrace(cause.getStackTrace()); 27 | 28 | if (cause.getCause() != null) { 29 | initCause(new MimicException(cause.getCause())); 30 | } 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | String s = className; 36 | String message = getLocalizedMessage(); 37 | return (message != null) ? (s + ": " + message) : s; 38 | } 39 | 40 | @Nullable 41 | public static Throwable make(@NonNull Channel ch, @Nullable Throwable cause) { 42 | if (cause == null) { 43 | return null; 44 | } 45 | 46 | // make sure the remoting layer of the other end supports this 47 | if (ch.remoteCapability.hasMimicException()) { 48 | return new MimicException(cause); 49 | } else { 50 | return cause; 51 | } 52 | } 53 | 54 | private static final long serialVersionUID = 1L; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/NamingThreadFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2013 Jesse Glick. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package hudson.remoting; 26 | 27 | import edu.umd.cs.findbugs.annotations.NonNull; 28 | import java.util.concurrent.Executors; 29 | import java.util.concurrent.ThreadFactory; 30 | import java.util.concurrent.atomic.AtomicInteger; 31 | 32 | /** 33 | * Thread factory that sets thread name so we know who is responsible for so many threads being created. 34 | * @since 2.52 35 | */ 36 | public class NamingThreadFactory implements ThreadFactory { 37 | private final AtomicInteger threadNum = new AtomicInteger(); 38 | private final ThreadFactory delegate; 39 | private final String name; 40 | 41 | /** 42 | * Creates a new naming factory. 43 | * @param delegate a baseline factory, such as {@link Executors#defaultThreadFactory} or {@link DaemonThreadFactory} 44 | * @param name an identifier to be used in thread names; might be e.g. your {@link Class#getSimpleName} 45 | */ 46 | public NamingThreadFactory(ThreadFactory delegate, String name) { 47 | this.delegate = delegate; 48 | this.name = name; 49 | } 50 | 51 | @Override 52 | public Thread newThread(@NonNull Runnable r) { 53 | Thread result = delegate.newThread(r); 54 | result.setName(String.format("%s [#%d]", name, threadNum.incrementAndGet())); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/RemotingSystemException.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | /** 4 | * Remote proxy uses this exception to signal the exception thrown 5 | * by the remoting layer and not by the application code that was invoked remotely. 6 | * 7 | *

8 | * The use of this exception triggers the caller to wrap a thrown exception to 9 | * add the stack trace that includes the call site information, making 10 | * the debugging easier 11 | * 12 | * 13 | * @author Kohsuke Kawaguchi 14 | */ 15 | class RemotingSystemException extends RuntimeException { 16 | RemotingSystemException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | RemotingSystemException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | private static final long serialVersionUID = 1L; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/RequestAbortedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | /** 27 | * Signals that the communication is aborted and thus 28 | * the pending {@link Request} will never recover its {@link Response}. 29 | * 30 | * @author Kohsuke Kawaguchi 31 | */ 32 | // this was added before RemotingSystemException. Going forward, it's probably 33 | // unnecessary to define specific subtypes of RemotingSystemException 34 | public class RequestAbortedException extends RemotingSystemException { 35 | public RequestAbortedException(Throwable cause) { 36 | super(cause); 37 | } 38 | 39 | private static final long serialVersionUID = 238929914783987425L; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/RequiredRoleCheckerWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2021, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import edu.umd.cs.findbugs.annotations.NonNull; 27 | import java.util.Collection; 28 | import org.jenkinsci.remoting.Role; 29 | import org.jenkinsci.remoting.RoleChecker; 30 | import org.jenkinsci.remoting.RoleSensitive; 31 | 32 | /** 33 | * Implementation that can tell whether a {@code #check} method was invoked. 34 | */ 35 | /* package-private */ class RequiredRoleCheckerWrapper extends RoleChecker { 36 | private boolean checked; 37 | 38 | private RoleChecker roleChecker; 39 | 40 | public RequiredRoleCheckerWrapper(RoleChecker roleChecker) { 41 | this.roleChecker = roleChecker; 42 | } 43 | 44 | @Override 45 | public void check(@NonNull RoleSensitive subject, Role... expected) throws SecurityException { 46 | checked = true; 47 | roleChecker.check(subject, expected); 48 | } 49 | 50 | @Override 51 | public void check(@NonNull RoleSensitive subject, @NonNull Role expected) throws SecurityException { 52 | checked = true; 53 | roleChecker.check(subject, expected); 54 | } 55 | 56 | @Override 57 | public void check(@NonNull RoleSensitive subject, @NonNull Collection expected) throws SecurityException { 58 | checked = true; 59 | roleChecker.check(subject, expected); 60 | } 61 | 62 | public boolean isChecked() { 63 | return checked; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ResourceImageBoth.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.util.concurrent.Future; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | /** 11 | * @author Kohsuke Kawaguchi 12 | */ 13 | class ResourceImageBoth extends ResourceImageDirect { 14 | final long sum1, sum2; 15 | 16 | public ResourceImageBoth(URL resource, Checksum sum) throws IOException { 17 | super(resource); 18 | this.sum1 = sum.sum1; 19 | this.sum2 = sum.sum2; 20 | } 21 | 22 | @Override 23 | Future resolve(Channel channel, String resourcePath) throws IOException, InterruptedException { 24 | initiateJarRetrieval(channel); 25 | return super.resolve(channel, resourcePath); 26 | } 27 | 28 | @Override 29 | Future resolveURL(Channel channel, String resourcePath) throws IOException, InterruptedException { 30 | Future f = initiateJarRetrieval(channel); 31 | if (f.isDone()) { // prefer using the jar URL if the stuff is already available 32 | return new ResourceImageInJar(sum1, sum2, null).resolveURL(channel, resourcePath); 33 | } else { 34 | return super.resolveURL(channel, resourcePath); 35 | } 36 | } 37 | 38 | /** 39 | * Starts JAR retrieval over the channel. 40 | * 41 | * @param channel Channel instance 42 | * @return Future object. In the case of error the diagnostics info will be sent to {@link #LOGGER}. 43 | */ 44 | @NonNull 45 | private Future initiateJarRetrieval(@NonNull Channel channel) throws IOException, InterruptedException { 46 | JarCache c = channel.getJarCache(); 47 | if (c == null) { 48 | throw new IOException( 49 | "Failed to initiate retrieval. JAR Cache is disabled for the channel " + channel.getName()); 50 | } 51 | 52 | try { 53 | return c.resolve(channel, sum1, sum2); 54 | } catch (IOException e) { 55 | LOGGER.log(Level.WARNING, "Failed to initiate retrieval", e); 56 | throw e; 57 | } catch (InterruptedException e) { 58 | LOGGER.log(Level.WARNING, "Failed to initiate retrieval", e); 59 | Thread.currentThread().interrupt(); // process the interrupt later 60 | throw e; 61 | } 62 | } 63 | 64 | private static final long serialVersionUID = 1L; 65 | 66 | private static final Logger LOGGER = Logger.getLogger(ResourceImageBoth.class.getName()); 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ResourceImageDirect.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.Future; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | /** 12 | * {@link ResourceImageRef} that directly encapsulates the resource as {@code byte[]}. 13 | * 14 | *

15 | * This is used when {@link ResourceImageInJar} cannot be used because we couldn't identify the jar file. 16 | * 17 | * @author Kohsuke Kawaguchi 18 | */ 19 | @SuppressFBWarnings( 20 | value = "URLCONNECTION_SSRF_FD", 21 | justification = "Used by the agent as part of jar cache management.") 22 | class ResourceImageDirect extends ResourceImageRef { 23 | /** 24 | * The actual resource. 25 | */ 26 | private final byte[] payload; 27 | 28 | ResourceImageDirect(byte[] payload) { 29 | this.payload = payload; 30 | } 31 | 32 | ResourceImageDirect(URL resource) throws IOException { 33 | this(Util.readFully(resource.openStream())); 34 | } 35 | 36 | @Override 37 | Future resolve(Channel channel, String resourcePath) throws IOException, InterruptedException { 38 | LOGGER.log(Level.FINE, resourcePath + " image is direct"); 39 | return CompletableFuture.completedFuture(payload); 40 | } 41 | 42 | @Override 43 | Future resolveURL(Channel channel, String resourcePath) throws IOException, InterruptedException { 44 | return CompletableFuture.completedFuture(URLish.from(Util.makeResource(resourcePath, payload))); 45 | } 46 | 47 | private static final Logger LOGGER = Logger.getLogger(ResourceImageDirect.class.getName()); 48 | 49 | private static final long serialVersionUID = 1L; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/ResourceImageRef.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.IOException; 4 | import java.io.Serializable; 5 | import java.util.concurrent.Future; 6 | 7 | /** 8 | * Wire protocol data representation that encapsulates the access to a resource inside a {@link ClassLoader}. 9 | * 10 | *

11 | * We support two kinds of references. One {@linkplain ResourceImageInJar points to a resource inside a jar}, 12 | * and the other {@linkplain ResourceImageDirect directly contains the byte[] image}. 13 | * 14 | *

15 | * Subtypes need to be available on both sides of the channel, so it's package protected. 16 | * 17 | * @author Kohsuke Kawaguchi 18 | */ 19 | /*package*/ abstract class ResourceImageRef implements Serializable { 20 | /** 21 | * Obtains the a resource file byte image. 22 | * 23 | * @param channel 24 | * The channel object as the context. 25 | * @param resourcePath 26 | * Fully qualified name of the resource being retrieved, that doesn't start with '/', 27 | * such as 'java/lang/String.class' Identical to the name parameter in {@link ClassLoader#getResource(String)}. 28 | * 29 | * This parameter must be the same name you used to retrieve {@link RemoteClassLoader.ClassReference}. 30 | * 31 | * One {@link ResourceImageRef} represents a single resource, and that resource name 32 | * is specified when you retrieve {@link RemoteClassLoader.ClassReference}. Therefore from pure abstraction 33 | * point of view, this information is redundant. However, specifying that information reduces 34 | * the amount of state {@link ResourceImageRef}s need to carry around, which helps reduce 35 | * the bandwidth consumption. 36 | */ 37 | /*package*/ abstract Future resolve(Channel channel, String resourcePath) 38 | throws IOException, InterruptedException; 39 | 40 | /** 41 | * Returns an URL that points to this resource. 42 | * 43 | * This may require creating a temporary file. 44 | */ 45 | /*package*/ abstract Future resolveURL(Channel channel, String resourcePath) 46 | throws IOException, InterruptedException; 47 | 48 | private static final long serialVersionUID = 1L; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/SocketInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import java.io.FilterInputStream; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.net.Socket; 30 | 31 | /** 32 | * {@link InputStream} connected to socket. 33 | * 34 | *

35 | * Unlike plain {@link Socket#getInputStream()}, closing the stream 36 | * does not close the entire socket, and instead it merely partial-close 37 | * a socket in the direction. 38 | * 39 | * @author Kohsuke Kawaguchi 40 | */ 41 | public class SocketInputStream extends FilterInputStream { 42 | private final Socket socket; 43 | 44 | /** 45 | * @deprecated 46 | * Use {@link SocketChannelStream#in(Socket)} 47 | */ 48 | @Deprecated 49 | public SocketInputStream(Socket socket) throws IOException { 50 | super(socket.getInputStream()); 51 | this.socket = socket; 52 | } 53 | 54 | @Override 55 | public void close() throws IOException { 56 | if (socket.isClosed()) { 57 | return; 58 | } 59 | if (!socket.isInputShutdown()) { 60 | socket.shutdownInput(); 61 | } 62 | if (socket.isOutputShutdown()) { 63 | socket.close(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/SynchronousExecutorService.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.concurrent.AbstractExecutorService; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * {@link ExecutorService} that executes synchronously. 12 | * 13 | * @author Kohsuke Kawaguchi 14 | */ 15 | class SynchronousExecutorService extends AbstractExecutorService { 16 | private volatile boolean shutdown = false; 17 | private int count = 0; 18 | 19 | @Override 20 | public void shutdown() { 21 | shutdown = true; 22 | } 23 | 24 | @Override 25 | @NonNull 26 | public List shutdownNow() { 27 | shutdown = true; 28 | return Collections.emptyList(); 29 | } 30 | 31 | @Override 32 | public boolean isShutdown() { 33 | return shutdown; 34 | } 35 | 36 | @Override 37 | public synchronized boolean isTerminated() { 38 | return shutdown && count == 0; 39 | } 40 | 41 | @Override 42 | public synchronized boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { 43 | long now = System.nanoTime(); 44 | long end = now + unit.toNanos(timeout); 45 | 46 | while (count != 0) { 47 | long d = end - now; 48 | if (d <= 0) { 49 | return false; 50 | } 51 | wait(TimeUnit.NANOSECONDS.toMillis(d)); 52 | now = System.nanoTime(); 53 | } 54 | return true; 55 | } 56 | 57 | @Override 58 | public void execute(@NonNull Runnable command) { 59 | if (shutdown) { 60 | throw new IllegalStateException("Already shut down"); 61 | } 62 | touchCount(1); 63 | try { 64 | command.run(); 65 | } finally { 66 | touchCount(-1); 67 | } 68 | } 69 | 70 | private synchronized void touchCount(int diff) { 71 | count += diff; 72 | if (count == 0) { 73 | notifyAll(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/URLish.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.CheckForNull; 4 | import edu.umd.cs.findbugs.annotations.NonNull; 5 | import java.io.File; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | 9 | /** 10 | * Something that's effectively URL. 11 | * 12 | *

13 | * Thie indirection on {@link URL} allows us to make sure that URLs backed by temporary files 14 | * actually do exist before we use them. 15 | * 16 | * @author Kohsuke Kawaguchi 17 | */ 18 | abstract class URLish { 19 | private URLish() {} 20 | 21 | /** 22 | * Converts URLish to the standard {@link URL} type. 23 | * @return URL or {@code null} if the target destination is known to be non-existent. 24 | * @throws MalformedURLException URL cannot be constructed 25 | */ 26 | @CheckForNull 27 | abstract URL toURL() throws MalformedURLException; 28 | 29 | @NonNull 30 | static URLish from(@NonNull final URL url) { 31 | 32 | return new URLish() { 33 | @Override 34 | @NonNull 35 | URL toURL() { 36 | return url; 37 | } 38 | }; 39 | } 40 | 41 | @NonNull 42 | static URLish from(@NonNull final File f) { 43 | return new URLish() { 44 | @Override 45 | URL toURL() throws MalformedURLException { 46 | // be defensive against external factors that might have deleted this file, since we use /tmp 47 | // see http://www.nabble.com/Surefire-reports-tt17554215.html 48 | if (f.exists()) { 49 | return f.toURI().toURL(); 50 | } 51 | return null; 52 | } 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/UnexportCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import edu.umd.cs.findbugs.annotations.CheckForNull; 27 | 28 | /** 29 | * {@link Command} that unexports an object. 30 | * @author Kohsuke Kawaguchi 31 | */ 32 | public class UnexportCommand extends Command { 33 | private final int oid; 34 | 35 | UnexportCommand(int oid, @CheckForNull Throwable cause) { 36 | this.oid = oid; 37 | chainCause(cause); 38 | } 39 | 40 | /** 41 | * @deprecated Use {@link #UnexportCommand(int, Throwable)} 42 | */ 43 | @Deprecated 44 | public UnexportCommand(int oid) { 45 | this(oid, null); 46 | } 47 | 48 | @Override 49 | protected void execute(Channel channel) { 50 | channel.unexport(oid, createdAt); 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "Unexport"; 56 | } 57 | 58 | private static final long serialVersionUID = 1L; 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/forward/CopyThread.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.forward; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | /** 10 | * Copies a stream and close them at EOF. 11 | * 12 | * @author Kohsuke Kawaguchi 13 | */ 14 | final class CopyThread extends Thread { 15 | private static final Logger LOGGER = Logger.getLogger(CopyThread.class.getName()); 16 | private final InputStream in; 17 | private final OutputStream out; 18 | 19 | /** 20 | * Callers are responsible for closing the input and output streams. 21 | */ 22 | public CopyThread(String threadName, InputStream in, OutputStream out, Runnable termination) { 23 | this(threadName, in, out, termination, 5); 24 | } 25 | 26 | private CopyThread(String threadName, InputStream in, OutputStream out, Runnable termination, int remainingTries) { 27 | super(threadName); 28 | this.in = in; 29 | this.out = out; 30 | setUncaughtExceptionHandler((t, e) -> { 31 | if (remainingTries > 0) { 32 | LOGGER.log(Level.WARNING, e, () -> "Uncaught exception in CopyThread " + t + ", retrying copy"); 33 | new CopyThread(threadName, in, out, termination, remainingTries - 1).start(); 34 | } else { 35 | LOGGER.log(Level.SEVERE, e, () -> "Uncaught exception in CopyThread " + t + ", out of retries"); 36 | termination.run(); 37 | } 38 | }); 39 | } 40 | 41 | @Override 42 | public void run() { 43 | try { 44 | byte[] buf = new byte[8192]; 45 | int len; 46 | while ((len = in.read(buf)) > 0) { 47 | out.write(buf, 0, len); 48 | } 49 | } catch (IOException e) { 50 | LOGGER.log(Level.WARNING, e, () -> "Exception while copying in thread: " + getName()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/forward/Forwarder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting.forward; 25 | 26 | import java.io.IOException; 27 | import java.io.OutputStream; 28 | import java.io.Serializable; 29 | 30 | /** 31 | * Abstracts away how the forwarding is set up. 32 | * 33 | * @author Kohsuke Kawaguchi 34 | */ 35 | public interface Forwarder extends Serializable { 36 | /** 37 | * Establishes a port forwarding connection and returns 38 | * the writer end. 39 | * 40 | * @param out 41 | * The writer end to the initiator. The callee will 42 | * start a thread that writes to this. 43 | */ 44 | OutputStream connect(OutputStream out) throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/forward/ListeningPort.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.forward; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Represents a listening port that forwards a connection 8 | * via port forwarding. 9 | * 10 | * @author Kohsuke Kawaguchi 11 | */ 12 | public interface ListeningPort extends Closeable { 13 | /** 14 | * TCP/IP port that is listening. 15 | */ 16 | int getPort(); 17 | 18 | /** 19 | * Shuts down the port forwarding by removing the server socket. 20 | * Connections that are already established will not be affected 21 | * by this operation. 22 | */ 23 | @Override 24 | void close() throws IOException; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/forward/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * TCP port forwarding over {@code hudson.remoting}. 3 | */ 4 | package hudson.remoting.forward; 5 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/jnlp/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting.jnlp; 25 | 26 | import hudson.remoting.Launcher; 27 | import java.io.IOException; 28 | import org.kohsuke.args4j.CmdLineException; 29 | 30 | /** 31 | * Previous entry point to pseudo-JNLP agent. 32 | * 33 | *

See also {@code jenkins-agent.jnlp.jelly} in the core. 34 | * 35 | * @author Kohsuke Kawaguchi 36 | * @deprecated use {@link Launcher} 37 | */ 38 | @Deprecated 39 | public class Main extends Launcher { 40 | 41 | private static volatile boolean deprecationWarningLogged; 42 | 43 | public static void main(String... args) throws IOException, InterruptedException { 44 | logDeprecation(); 45 | Launcher.main(args); 46 | } 47 | 48 | @Override 49 | public void run() throws CmdLineException, IOException, InterruptedException { 50 | logDeprecation(); 51 | super.run(); 52 | } 53 | 54 | private static void logDeprecation() { 55 | if (deprecationWarningLogged) { 56 | return; 57 | } 58 | System.err.println( 59 | "WARNING: Using deprecated entrypoint \"java -cp agent.jar hudson.remoting.jnlp.Main\". Use \"java -jar agent.jar\" instead."); 60 | deprecationWarningLogged = true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/jnlp/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | /** 25 | * Code for launching the remote agent through JNLP. 26 | * 27 | *

28 | * This involves in getting the connection parameters through command line, 29 | * which is provided from {@code jenkins-agent.jnlp.jelly} file in the core. 30 | */ 31 | package hudson.remoting.jnlp; 32 | -------------------------------------------------------------------------------- /src/main/java/hudson/remoting/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | /** 25 | * Remoting infrastructure for Hudson. 26 | * 27 | *

28 | * Code in this package is used for running a part of a program in agents. 29 | * If you are new to this package, start from {@link hudson.remoting.Channel}. 30 | */ 31 | package hudson.remoting; 32 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/CallableDecorator.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting; 2 | 3 | import hudson.remoting.Callable; 4 | import hudson.remoting.Channel; 5 | 6 | /** 7 | * Decorator on {@code Callable.call()} to filter the execution. 8 | * 9 | * @author Kohsuke Kawaguchi 10 | * @see Channel#addLocalExecutionInterceptor(CallableDecorator) 11 | */ 12 | public abstract class CallableDecorator { 13 | /** 14 | * Used to decorate everything that executes in the channel as a result of a request from the other side, 15 | * such as RPC executions on exported objects, user-provided {@link Callable} execution, pipe write, and so on. 16 | */ 17 | public V call(java.util.concurrent.Callable callable) throws Exception { 18 | return callable.call(); 19 | } 20 | 21 | /** 22 | * Used to specifically decorate user-provided {@link Callable} execution. 23 | * 24 | * Unlike {@link #call(java.util.concurrent.Callable)}, this method provides an opportunity 25 | * to inspect the actual {@link Callable} object given to {@link Channel#call(Callable)} 26 | * from the other side, whereas {@link #call(java.util.concurrent.Callable)} only 27 | * provides an opaque blob that itself may wrap the actual user-given operations. 28 | * 29 | * @param op 30 | * The original callable object given to {@link Channel#call(Callable)}. 31 | * @param stem 32 | * Computation that represents the invocation of {@code op} as well as any additional decoration done by other 33 | * {@link CallableDecorator}s. 34 | * @return 35 | * Returns the a decorated {@link Callable} that represents the decorated computation, 36 | * which normally executes some pre-processing, then delegates to the {@code stem}, then performs some cleanup. 37 | * 38 | * If there's nothing to filter, return {@code stem}. 39 | * @throws RuntimeException 40 | * Any exception thrown from this method will be propagated to the other side as if the execution of 41 | * the callable had failed with this exception. 42 | */ 43 | public Callable userRequest(Callable op, Callable stem) { 44 | return stem; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/DurationOptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2024, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting; 25 | 26 | import java.time.Duration; 27 | import org.jenkinsci.remoting.util.DurationFormatter; 28 | import org.jenkinsci.remoting.util.DurationStyle; 29 | import org.kohsuke.accmod.Restricted; 30 | import org.kohsuke.accmod.restrictions.NoExternalUse; 31 | import org.kohsuke.args4j.CmdLineException; 32 | import org.kohsuke.args4j.CmdLineParser; 33 | import org.kohsuke.args4j.OptionDef; 34 | import org.kohsuke.args4j.spi.OptionHandler; 35 | import org.kohsuke.args4j.spi.Parameters; 36 | import org.kohsuke.args4j.spi.Setter; 37 | 38 | /** 39 | * Parses a string like 1s, 2m, 3h, 4d into a {@link Duration}. 40 | */ 41 | @Restricted(NoExternalUse.class) 42 | public class DurationOptionHandler extends OptionHandler { 43 | public DurationOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { 44 | super(parser, option, setter); 45 | } 46 | 47 | @Override 48 | public int parseArguments(Parameters params) throws CmdLineException { 49 | setter.addValue(DurationStyle.detectAndParse(params.getParameter(0))); 50 | return 1; 51 | } 52 | 53 | @Override 54 | public String getDefaultMetaVariable() { 55 | return "DURATION"; 56 | } 57 | 58 | @Override 59 | protected String print(Duration v) { 60 | return DurationFormatter.format(v); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/RoleChecker.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting; 2 | 3 | import edu.umd.cs.findbugs.annotations.NonNull; 4 | import hudson.remoting.Callable; 5 | import hudson.remoting.ChannelBuilder; 6 | import java.util.Arrays; 7 | import java.util.Collection; 8 | import java.util.Set; 9 | 10 | /** 11 | * Verifies that the callable is getting run on the intended recipient. 12 | * 13 | * @author Kohsuke Kawaguchi 14 | * @see ChannelBuilder#withRoleChecker(RoleChecker) 15 | * @since 2.47 16 | */ 17 | public abstract class RoleChecker { 18 | /** 19 | * Called from {@link RoleSensitive#checkRoles(RoleChecker)} to ensure that this side of the channel 20 | * is willing to execute {@link Callable}s that expects one of the given roles on their intended recipients. 21 | *

If you think you need to implement {@link RoleSensitive#checkRoles} please reread that method’s Javadoc. 22 | *

23 | * Normally, each side of the channel has a fixed set of roles (say {@code actualRoles}), 24 | * and the implementation would be {@code actualRoles.containsAll(roles)}. 25 | * 26 | * @param subject 27 | * Object whose role we are checking right now. Useful context information when reporting an error. 28 | * @param expected 29 | * The current JVM that executes the callable should have one of these roles. 30 | * Never empty nor null. 31 | * @throws SecurityException 32 | * Any exception thrown will prevent the callable from getting executed, but we recommend 33 | * {@link SecurityException} 34 | */ 35 | public abstract void check(@NonNull RoleSensitive subject, @NonNull Collection expected) 36 | throws SecurityException; 37 | 38 | public void check(@NonNull RoleSensitive subject, @NonNull Role expected) throws SecurityException { 39 | check(subject, Set.of(expected)); 40 | } 41 | 42 | public void check(@NonNull RoleSensitive subject, Role... expected) throws SecurityException { 43 | check(subject, Arrays.asList(expected)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/RoleSensitive.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting; 2 | 3 | import hudson.remoting.Callable; 4 | import java.util.Collection; 5 | 6 | /** 7 | * Used by {@link Callable}-like objects to designate the intended recipient of the callable, 8 | * to help verify callables are running in JVMs that it is intended to run. 9 | * 10 | *

11 | * This interface is defined separately from {@link Callable} so that other callable-like interfaces 12 | * can reuse this. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | * @see RoleChecker 16 | * @since 2.47 17 | */ 18 | public interface RoleSensitive { 19 | /** 20 | * Verifies the roles expected by this callable by invoking {@link RoleChecker#check(RoleSensitive, Collection)} 21 | * method (or its variants), to provide an opportunity for {@link RoleChecker} to reject this object. 22 | *

Do not implement this method unless you know what you are doing. 23 | * If you have a Jenkins {@link Callable} or {@code FileCallable}, use the standard abstract base classes instead, 24 | * such as {@code MasterToSlaveCallable}, {@code MasterToSlaveFileCallable}, {@code NotReallyRoleSensitiveCallable}, etc. 25 | * See this document for details. 26 | *

27 | * If the method returns normally, the check has passed. 28 | * 29 | * @throws SecurityException 30 | * If there's a mismatch in the expected roles and the actual roles that should prevent 31 | * the execution of this callable. 32 | * 33 | * @throws AbstractMethodError 34 | * In the history of this library, this interface was added rather later, so there's lots of 35 | * {@link Callable}s out there that do not implement this method. 36 | * For this reason, code that calls this method should be prepared to 37 | * receive {@link AbstractMethodError}, and treat that as if the invocation of 38 | * {@code checker.check(this,Role.UNKNOWN)} has happened. 39 | */ 40 | void checkRoles(RoleChecker checker) throws SecurityException; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/engine/HostPort.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.engine; 2 | 3 | class HostPort { 4 | 5 | private static final int PORT_MIN = 0; 6 | private static final int PORT_MAX = (1 << 16) - 1; 7 | 8 | private String host; 9 | private int port; 10 | 11 | public HostPort(String value) { 12 | splitHostPort(value, null, 0); 13 | } 14 | 15 | public HostPort(String value, String defaultHost, int defaultPort) { 16 | splitHostPort(value, defaultHost, defaultPort); 17 | } 18 | 19 | private void splitHostPort(String value, String defaultHost, int defaultPort) { 20 | String hostPortValue = value.trim(); 21 | if (hostPortValue.charAt(0) == '[') { 22 | extractIPv6(hostPortValue); 23 | return; 24 | } 25 | int portSeparator = hostPortValue.lastIndexOf(':'); 26 | if (portSeparator < 0) { 27 | throw new IllegalArgumentException("Invalid HOST:PORT value: " + value); 28 | } 29 | String hostValue = hostPortValue.substring(0, portSeparator).trim(); 30 | host = !hostValue.isEmpty() ? hostValue : defaultHost; 31 | String portString = hostPortValue.substring(portSeparator + 1).trim(); 32 | if (!portString.isEmpty()) { 33 | port = Integer.parseInt(portString); 34 | if (port <= PORT_MIN || port > PORT_MAX) { 35 | throw new IllegalArgumentException( 36 | "Port " + value + " out of valid range [" + PORT_MIN + ", " + PORT_MAX + ")"); 37 | } 38 | } else { 39 | port = defaultPort; 40 | } 41 | } 42 | 43 | private void extractIPv6(String hostPortValue) { 44 | int endBracket = hostPortValue.indexOf(']'); 45 | if (endBracket < 2) { 46 | throw new IllegalArgumentException("Invalid IPv6 value."); 47 | } 48 | host = hostPortValue.substring(1, endBracket).trim(); 49 | int portSeparator = hostPortValue.lastIndexOf(':'); 50 | if (portSeparator < endBracket) { 51 | throw new IllegalArgumentException("Missing port."); 52 | } 53 | port = Integer.parseInt(hostPortValue.substring(portSeparator + 1).trim()); 54 | } 55 | 56 | public String getHost() { 57 | return host; 58 | } 59 | 60 | public int getPort() { 61 | return port; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/engine/JnlpEndpointResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2019, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.engine; 25 | 26 | import java.io.IOException; 27 | import java.security.KeyFactory; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.interfaces.RSAPublicKey; 30 | import java.security.spec.InvalidKeySpecException; 31 | import java.security.spec.X509EncodedKeySpec; 32 | import java.util.Base64; 33 | 34 | public abstract class JnlpEndpointResolver { 35 | 36 | public abstract JnlpAgentEndpoint resolve() throws IOException; 37 | 38 | public abstract void waitForReady() throws InterruptedException; 39 | 40 | protected RSAPublicKey getIdentity(String base64EncodedIdentity) throws InvalidKeySpecException { 41 | if (base64EncodedIdentity == null) { 42 | return null; 43 | } 44 | try { 45 | byte[] encodedKey = Base64.getDecoder().decode(base64EncodedIdentity); 46 | if (encodedKey == null) { 47 | return null; 48 | } 49 | X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedKey); 50 | KeyFactory kf = KeyFactory.getInstance("RSA"); 51 | return (RSAPublicKey) kf.generatePublic(spec); 52 | } catch (NoSuchAlgorithmException e) { 53 | throw new IllegalStateException( 54 | "The Java Language Specification mandates RSA as a supported algorithm.", e); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/nio/Closeables.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.nio; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.net.Socket; 6 | import java.nio.channels.SelectableChannel; 7 | import java.nio.channels.SocketChannel; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | /** 12 | * Creates {@link Closeable} that does socket half-close. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | */ 16 | class Closeables { 17 | public static Closeable input(SelectableChannel ch) { 18 | if (ch instanceof SocketChannel) { 19 | final SocketChannel s = (SocketChannel) ch; 20 | return () -> { 21 | try { 22 | s.socket().shutdownInput(); 23 | } catch (IOException e) { 24 | // at least as of Java7u55, shutdownInput fails if the socket 25 | // is already closed or half-closed, as opposed to be a no-op. 26 | // so let's just ignore close error altogether 27 | LOGGER.log(Level.FINE, "Failed to close " + s, e); 28 | } 29 | maybeClose(s); 30 | }; 31 | } else { 32 | return ch; 33 | } 34 | } 35 | 36 | public static Closeable output(SelectableChannel ch) { 37 | if (ch instanceof SocketChannel) { 38 | final SocketChannel s = (SocketChannel) ch; 39 | return () -> { 40 | try { 41 | s.socket().shutdownOutput(); 42 | } catch (IOException e) { 43 | // see the discussion in try/catch block around shutdownInput above 44 | LOGGER.log(Level.FINE, "Failed to close " + s, e); 45 | } 46 | maybeClose(s); 47 | }; 48 | } else { 49 | return ch; 50 | } 51 | } 52 | 53 | /** 54 | * If both direction is closed, close the whole thing. 55 | */ 56 | private static void maybeClose(SocketChannel sc) throws IOException { 57 | Socket s = sc.socket(); 58 | if (s.isInputShutdown() && s.isOutputShutdown()) { 59 | s.close(); 60 | sc.close(); 61 | } 62 | } 63 | 64 | private static final Logger LOGGER = Logger.getLogger(Closeables.class.getName()); 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/nio/SelectorThreadOnly.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.nio; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Marks the methods that can be only executed by the NIO selector thread. 10 | * 11 | *

Rules

12 | *
    13 | *
  • If the base method has this annotation, all the overriding methods must have this. 14 | *
  • Only the caller that's marked as {@link SelectorThreadOnly} can call these methods. 15 | *
16 | * 17 | * @author Kohsuke Kawaguchi 18 | */ 19 | @Target(ElementType.METHOD) 20 | @Retention(RetentionPolicy.SOURCE) 21 | @interface SelectorThreadOnly {} 22 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/protocol/IOHubRegistrationCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc., Stephen Connolly 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol; 25 | 26 | import java.nio.channels.ClosedChannelException; 27 | import java.nio.channels.SelectableChannel; 28 | import java.nio.channels.SelectionKey; 29 | 30 | /** 31 | * Callback to be notified when a call to 32 | * {@link IOHub#register(SelectableChannel, IOHubReadyListener, boolean, boolean, boolean, boolean, IOHubRegistrationCallback)} has completed registration. 33 | * 34 | * @since 3.0 35 | */ 36 | public interface IOHubRegistrationCallback { 37 | 38 | /** 39 | * Notification callback that the {@link SelectableChannel} has been successfully registered. 40 | * 41 | * @param selectionKey the {@link SelectionKey} that the {@link SelectableChannel} was registered as. 42 | */ 43 | void onRegistered(SelectionKey selectionKey); 44 | 45 | /** 46 | * Notification callback that the {@link SelectableChannel} was closed by the time the registration request was 47 | * processed. 48 | * 49 | * @param e the {@link ClosedChannelException} that was thrown when the registration request was attempted. 50 | */ 51 | void onClosedChannel(ClosedChannelException e); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/protocol/IOHubRegistrationFutureAdapterImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc., Stephen Connolly 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol; 25 | 26 | import hudson.remoting.Future; 27 | import java.nio.channels.ClosedChannelException; 28 | import java.nio.channels.SelectionKey; 29 | import org.jenkinsci.remoting.util.SettableFuture; 30 | 31 | /** 32 | * Converts the {@link IOHubRegistrationCallback} pattern into a {@link Future}. 33 | * 34 | * @since 3.0 35 | */ 36 | class IOHubRegistrationFutureAdapterImpl implements IOHubRegistrationCallback { 37 | /** 38 | * The future. 39 | */ 40 | private final SettableFuture future = SettableFuture.create(); 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public void onRegistered(SelectionKey selectionKey) { 47 | future.set(selectionKey); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | @Override 54 | public void onClosedChannel(ClosedChannelException e) { 55 | future.setException(e); 56 | } 57 | 58 | /** 59 | * Gets the future. 60 | * 61 | * @return the future. 62 | */ 63 | public SettableFuture getFuture() { 64 | return future; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/protocol/impl/PermanentConnectionRefusalException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, Stephen Connolly, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol.impl; 25 | 26 | /** 27 | * An exception to flag that the connection has been rejected and no further connection attempts should be made. 28 | * @deprecated Does not actually do what it claims; only affects logging levels. 29 | * @since 3.0 30 | */ 31 | @Deprecated 32 | public class PermanentConnectionRefusalException extends ConnectionRefusalException { 33 | public PermanentConnectionRefusalException() { 34 | super(); 35 | } 36 | 37 | public PermanentConnectionRefusalException(String message) { 38 | super(message); 39 | } 40 | 41 | public PermanentConnectionRefusalException(String message, Object... args) { 42 | super(message, args); 43 | } 44 | 45 | public PermanentConnectionRefusalException(Throwable cause, String message, Object... args) { 46 | super(cause, message, args); 47 | } 48 | 49 | public PermanentConnectionRefusalException(String message, Throwable cause) { 50 | super(message, cause); 51 | } 52 | 53 | public PermanentConnectionRefusalException(Throwable cause) { 54 | super(cause); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/ByteBufferPool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.util; 25 | 26 | import java.nio.ByteBuffer; 27 | 28 | /** 29 | * API for a pool of {@link ByteBuffer}s. 30 | */ 31 | public interface ByteBufferPool { 32 | /** 33 | * Borrow a buffer from the pool. 34 | * 35 | * @param size The minimum size and initial limit of the buffer. 36 | * @return the buffer. 37 | */ 38 | ByteBuffer acquire(int size); 39 | 40 | /** 41 | * Returns a buffer to the pool. 42 | * 43 | * @param buffer the buffer. 44 | */ 45 | void release(ByteBuffer buffer); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/ByteBufferQueueOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.util; 25 | 26 | import edu.umd.cs.findbugs.annotations.NonNull; 27 | import java.io.IOException; 28 | import java.io.OutputStream; 29 | 30 | /** 31 | * An {@link OutputStream} backed by a {@link ByteBufferQueue}. 32 | * 33 | * @since 3.0 34 | */ 35 | public class ByteBufferQueueOutputStream extends OutputStream { 36 | 37 | /** 38 | * The backing queue. 39 | */ 40 | private final ByteBufferQueue queue; 41 | 42 | /** 43 | * Creates a new instance. 44 | * 45 | * @param queue the backing queue. 46 | */ 47 | public ByteBufferQueueOutputStream(ByteBufferQueue queue) { 48 | this.queue = queue; 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | @Override 55 | public void write(int b) throws IOException { 56 | queue.put((byte) b); 57 | } 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | @Override 63 | public void write(@NonNull byte[] b, int off, int len) throws IOException { 64 | queue.put(b, off, len); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/IOUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.util; 25 | 26 | import java.io.Closeable; 27 | import java.io.IOException; 28 | import org.kohsuke.accmod.Restricted; 29 | import org.kohsuke.accmod.restrictions.NoExternalUse; 30 | 31 | /** 32 | * I/O related utility methods. Included to minimize external dependencies of the remoting library. 33 | * 34 | * @since 3.0 35 | */ 36 | @Restricted(NoExternalUse.class) 37 | public class IOUtils { 38 | 39 | /** 40 | * Utility class. 41 | */ 42 | private IOUtils() { 43 | throw new IllegalAccessError("Utility class"); 44 | } 45 | 46 | /** 47 | * Unconditionally close a Closeable. 48 | * 49 | * @param closeable the object to close, may be null or already closed 50 | * @since 3.0 51 | */ 52 | public static void closeQuietly(Closeable closeable) { 53 | try { 54 | if (closeable != null) { 55 | closeable.close(); 56 | } 57 | } catch (IOException ioe) { 58 | // ignore 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/PathUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * The MIT License 4 | * 5 | * Copyright (c) 2017 CloudBees, Inc. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | package org.jenkinsci.remoting.util; 28 | 29 | import edu.umd.cs.findbugs.annotations.NonNull; 30 | import java.io.File; 31 | import java.io.IOException; 32 | import java.nio.file.InvalidPathException; 33 | import java.nio.file.Path; 34 | import org.kohsuke.accmod.Restricted; 35 | import org.kohsuke.accmod.restrictions.NoExternalUse; 36 | 37 | /** 38 | * Utilities for {@link Path} handling. 39 | */ 40 | @Restricted(NoExternalUse.class) 41 | public class PathUtils { 42 | 43 | private PathUtils() {} 44 | 45 | /** 46 | * Converts {@link File} to {@link Path} and checks runtime exceptions. 47 | * @param file File 48 | * @return Resulting path 49 | * @throws IOException Conversion error caused by {@link InvalidPathException} 50 | * @since 3.14 51 | */ 52 | @NonNull 53 | @Restricted(NoExternalUse.class) 54 | public static Path fileToPath(@NonNull File file) throws IOException { 55 | try { 56 | return file.toPath(); 57 | } catch (InvalidPathException ex) { 58 | throw new IOException(ex); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/ThrowableUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.util; 25 | 26 | import edu.umd.cs.findbugs.annotations.CheckForNull; 27 | 28 | /** 29 | * Utility methods to help when working with {@link Throwable} instances. 30 | * 31 | * @since 3.0 32 | */ 33 | public class ThrowableUtils { 34 | 35 | /** 36 | * This is a utility class, prevent accidental instance creation. 37 | */ 38 | private ThrowableUtils() { 39 | throw new IllegalAccessError("Utility class"); 40 | } 41 | 42 | /** 43 | * Allows building a chain of exceptions. 44 | * 45 | * @param e1 The first exception (or {@code null}). 46 | * @param e2 The second exception (or {@code null}). 47 | * @param The widened return type. 48 | * @param The type of first exception. 49 | * @param The type of second exception. 50 | * @return The first exception with the second added as a suppressed exception or the closest approximation to that. 51 | */ 52 | @CheckForNull 53 | public static T chain(@CheckForNull T1 e1, @CheckForNull T2 e2) { 54 | if (e1 == null) { 55 | return e2; 56 | } 57 | if (e2 == null) { 58 | return e1; 59 | } 60 | e1.addSuppressed(e2); 61 | return e1; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/https/NoCheckHostnameVerifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * The MIT License 4 | * 5 | * Copyright (c) 2017 CloudBees, Inc. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | package org.jenkinsci.remoting.util.https; 28 | 29 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 30 | import javax.net.ssl.HostnameVerifier; 31 | import javax.net.ssl.SSLSession; 32 | import org.kohsuke.accmod.Restricted; 33 | import org.kohsuke.accmod.restrictions.NoExternalUse; 34 | 35 | /** 36 | * Hostname verifier, which accepts any hostname. 37 | */ 38 | @Restricted(NoExternalUse.class) 39 | @SuppressFBWarnings(value = "WEAK_HOSTNAME_VERIFIER", justification = "User set parameter to skip verifier.") 40 | public class NoCheckHostnameVerifier implements HostnameVerifier { 41 | 42 | @Override 43 | public boolean verify(String s, SSLSession sslSession) { 44 | return true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/remoting/util/https/NoCheckTrustManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * The MIT License 4 | * 5 | * Copyright (c) 2016- CloudBees, Inc. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | package org.jenkinsci.remoting.util.https; 28 | 29 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 30 | import java.security.cert.X509Certificate; 31 | import javax.net.ssl.X509TrustManager; 32 | import org.kohsuke.accmod.Restricted; 33 | import org.kohsuke.accmod.restrictions.NoExternalUse; 34 | 35 | /** 36 | * {@link X509TrustManager} that performs no check at all. 37 | */ 38 | @Restricted(NoExternalUse.class) 39 | @SuppressFBWarnings(value = "WEAK_TRUST_MANAGER", justification = "User set parameter to skip verifier.") 40 | public class NoCheckTrustManager implements X509TrustManager { 41 | @Override 42 | public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {} 43 | 44 | @Override 45 | public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} 46 | 47 | @Override 48 | public X509Certificate[] getAcceptedIssuers() { 49 | return new X509Certificate[0]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/JarCertDump.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.io.IOException; 3 | import java.nio.charset.StandardCharsets; 4 | import java.security.cert.Certificate; 5 | import java.util.jar.JarEntry; 6 | import java.util.jar.JarFile; 7 | import org.apache.commons.io.IOUtils; 8 | 9 | /** 10 | * Tool to parse the certificate chain in the jar file. 11 | * 12 | * @author Kohsuke Kawaguchi 13 | */ 14 | public class JarCertDump { 15 | public static void main(String[] args) throws IOException { 16 | try (JarFile j = new JarFile(new File(args[0]))) { 17 | JarEntry je = j.getJarEntry("hudson/remoting/Channel.class"); 18 | if (je == null) { 19 | throw new IllegalArgumentException(); 20 | } 21 | IOUtils.readLines(j.getInputStream(je), StandardCharsets.UTF_8); 22 | for (Certificate c : je.getCertificates()) { 23 | System.out.println("################# Certificate #################"); 24 | System.out.println(c); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/OISInterception.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayInputStream; 2 | import java.io.ByteArrayOutputStream; 3 | import java.io.IOException; 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.io.ObjectStreamClass; 7 | import java.util.Set; 8 | 9 | /** 10 | * Experimenting with inspecting stream contents as it is read. 11 | * 12 | * @author Kohsuke Kawaguchi 13 | */ 14 | public class OISInterception { 15 | public static void main(String[] args) throws Exception { 16 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 17 | try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 18 | oos.writeObject(Set.of("foo")); 19 | } 20 | 21 | ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) { 22 | @Override 23 | protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { 24 | ObjectStreamClass d = super.readClassDescriptor(); 25 | // this can be used to filter out classes 26 | System.out.println(d.getName()); 27 | return d; 28 | } 29 | }; 30 | 31 | System.out.println(ois.readObject()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/TrafficAnalyzer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * See {@link hudson.remoting.TrafficAnalyzer}. This entry point makes it easier to 3 | * invoke the tool. 4 | * 5 | * @author Kohsuke Kawaguchi 6 | */ 7 | public class TrafficAnalyzer { 8 | public static void main(String[] args) throws Exception { 9 | hudson.remoting.TrafficAnalyzer.main(args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/AbstractNioChannelRunner.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import org.jenkinsci.remoting.nio.NioChannelHub; 6 | 7 | /** 8 | * @author Kohsuke Kawaguchi 9 | */ 10 | public abstract class AbstractNioChannelRunner implements DualSideChannelRunner { 11 | protected ExecutorService executor = Executors.newCachedThreadPool(); 12 | protected NioChannelHub nio; 13 | /** 14 | * failure occurred in the other {@link Channel}. 15 | */ 16 | protected Throwable failure; 17 | 18 | protected Channel south; 19 | 20 | @Override 21 | public void stop(Channel channel) throws Exception { 22 | channel.close(); 23 | channel.join(); 24 | 25 | System.out.println("north completed"); 26 | 27 | // we initiate the shutdown from north, so by the time it closes south should be all closed, too 28 | /* TODO passes reliably on Java 7 but often fails on Java 8 29 | assertTrue(south.isInClosed()); 30 | assertTrue(south.isOutClosed()); 31 | */ 32 | 33 | nio.close(); 34 | executor.shutdown(); 35 | 36 | if (failure != null) { 37 | throw new AssertionError(failure); // report a failure in the south side 38 | } 39 | } 40 | 41 | @Override 42 | public Channel getOtherSide() { 43 | return south; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return getName(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/CallableBase.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import org.jenkinsci.remoting.Role; 4 | import org.jenkinsci.remoting.RoleChecker; 5 | 6 | /** 7 | * @author Kohsuke Kawaguchi 8 | */ 9 | public abstract class CallableBase implements Callable { 10 | @Override 11 | public void checkRoles(RoleChecker checker) throws SecurityException { 12 | checker.check(this, ROLE); 13 | } 14 | 15 | public static final Role ROLE = new Role("test callable"); 16 | private static final long serialVersionUID = 1L; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/ChannelRunners.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.util.stream.Stream; 4 | 5 | public final class ChannelRunners { 6 | public static final String PROVIDER_METHOD = "hudson.remoting.ChannelRunners#provider"; 7 | 8 | private ChannelRunners() {} 9 | 10 | @SuppressWarnings("unused") // used by JUnit 11 | public static Stream provider() { 12 | return Stream.of( 13 | new InProcessRunner(), 14 | new NioSocketRunner(), 15 | new NioPipeRunner(), 16 | new InProcessCompatibilityRunner(), 17 | new ForkRunner(), 18 | new ForkEBCDICRunner()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/Copier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.io.OutputStream; 29 | 30 | /** 31 | * Thread that copies a stream. 32 | * 33 | * @author Kohsuke Kawaguchi 34 | */ 35 | class Copier extends Thread { 36 | private final InputStream in; 37 | private final OutputStream out; 38 | 39 | public Copier(String threadName, InputStream in, OutputStream out) { 40 | super(threadName); 41 | this.in = in; 42 | this.out = out; 43 | } 44 | 45 | @Override 46 | public void run() { 47 | try { 48 | byte[] buf = new byte[8192]; 49 | int len; 50 | while ((len = in.read(buf)) > 0) { 51 | out.write(buf, 0, len); 52 | } 53 | in.close(); 54 | } catch (IOException e) { 55 | // TODO: what to do? 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/DualSideChannelRunner.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | /** 4 | * Subset of {@link ChannelRunner} that provides in-memory access 5 | * to the other side, not just this side returned by {@link #start()} 6 | * 7 | * @author Kohsuke Kawaguchi 8 | */ 9 | public interface DualSideChannelRunner extends ChannelRunner { 10 | Channel getOtherSide(); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/DummyClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import static org.junit.jupiter.api.Assertions.assertTrue; 27 | 28 | import org.junit.jupiter.api.Test; 29 | 30 | /** 31 | * @author Kohsuke Kawaguchi 32 | */ 33 | public class DummyClassLoaderTest { 34 | @Test 35 | public void testLoad() throws Throwable { 36 | Callable c = (Callable) DummyClassLoader.apply(TestCallable.class); 37 | System.out.println(c.call()); 38 | // make sure that the returned class is loaded from the dummy classloader 39 | assertTrue(((Object[]) c.call())[0].toString().startsWith(DummyClassLoader.class.getName())); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/ExportTableTest.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.io.PrintWriter; 8 | import java.io.StringWriter; 9 | import java.util.concurrent.ExecutionException; 10 | import org.junit.jupiter.api.Test; 11 | 12 | /** 13 | * @author Kohsuke Kawaguchi 14 | */ 15 | public class ExportTableTest { 16 | @Test 17 | public void testDiagnosis() throws Exception { 18 | try { 19 | ExportTable.EXPORT_TRACES = true; 20 | ExportTable e = new ExportTable(); 21 | 22 | int i = e.export(Object.class, "foo"); 23 | assertEquals("foo", e.get(i)); 24 | 25 | e.unexportByOid(i); 26 | 27 | final ExecutionException x = assertThrows(ExecutionException.class, () -> e.get(i)); 28 | StringWriter sw = new StringWriter(); 29 | x.printStackTrace(new PrintWriter(sw)); 30 | assertTrue(sw.toString().contains("Object was recently deallocated")); 31 | assertTrue(sw.toString().contains("ExportTable.export")); 32 | } finally { 33 | ExportTable.EXPORT_TRACES = false; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/ForkEBCDICRunner.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * {@link ForkRunner} with an ASCII incompatible encoding. 7 | */ 8 | public class ForkEBCDICRunner extends ForkRunner { 9 | @Override 10 | protected List buildCommandLine() { 11 | List r = super.buildCommandLine(); 12 | r.add(0, "-Dfile.encoding=CP037"); 13 | return r; 14 | } 15 | 16 | @Override 17 | public String getName() { 18 | return "forkEBCDIC"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/ForkRunner.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.io.OutputStream; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | 11 | /** 12 | * Runs a channel in a separate JVM by launching a new JVM. 13 | */ 14 | public class ForkRunner implements ChannelRunner { 15 | private Process proc; 16 | private ExecutorService executor; 17 | private Copier copier; 18 | 19 | protected List buildCommandLine() { 20 | String cp = getClasspath(); 21 | 22 | List r = new ArrayList<>(); 23 | r.add("-Xmx128M"); 24 | r.add("-cp"); 25 | r.add(cp); 26 | r.add(Launcher.class.getName()); 27 | return r; 28 | } 29 | 30 | @Override 31 | public Channel start() throws Exception { 32 | List cmds = buildCommandLine(); 33 | cmds.add(0, "java"); 34 | proc = Runtime.getRuntime().exec(cmds.toArray(new String[0])); 35 | 36 | copier = new Copier("copier", proc.getErrorStream(), System.out); 37 | copier.start(); 38 | 39 | executor = Executors.newCachedThreadPool(); 40 | OutputStream out = proc.getOutputStream(); 41 | return new ChannelBuilder("north", executor).build(proc.getInputStream(), out); 42 | } 43 | 44 | @Override 45 | public void stop(Channel channel) throws Exception { 46 | channel.close(); 47 | channel.join(10 * 1000); 48 | 49 | executor.shutdown(); 50 | 51 | copier.join(); 52 | int r = proc.waitFor(); 53 | 54 | assertEquals("exit code should have been 0", 0, r); 55 | } 56 | 57 | @Override 58 | public String getName() { 59 | return "fork"; 60 | } 61 | 62 | public String getClasspath() { 63 | return System.getProperty("java.class.path"); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return getName(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/HexDumpTest.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author Kohsuke Kawaguchi 9 | */ 10 | public class HexDumpTest { 11 | @Test 12 | public void test1() { 13 | assertEquals("0x00 0x01 0xff 'A'", HexDump.toHex(new byte[] {0, 1, -1, 65})); 14 | assertEquals("0x00 0x01 0xff 'ABC'", HexDump.toHex(new byte[] {0, 1, -1, 65, 66, 67})); 15 | assertEquals("'AAAA' 0x00", HexDump.toHex(new byte[] {65, 65, 65, 65, 0})); 16 | } 17 | 18 | @Test 19 | public void testMultiline() { 20 | assertEquals("'A A' 0x0a\n' AA'", HexDump.toHex(new byte[] {65, 32, 65, 10, 32, 65, 65})); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/InProcessCompatibilityRunner.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | /** 4 | * @author Kohsuke Kawaguchi 5 | */ 6 | public class InProcessCompatibilityRunner extends InProcessRunner { 7 | @Override 8 | public String getName() { 9 | return "local-compatibility"; 10 | } 11 | 12 | @Override 13 | protected Capability createCapability() { 14 | return Capability.NONE; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/NioPipeRunner.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.nio.channels.Pipe; 4 | import java.util.concurrent.SynchronousQueue; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | import org.jenkinsci.remoting.nio.NioChannelHub; 9 | 10 | /** 11 | * Runs a channel over NIO {@link Pipe}. 12 | * 13 | *

14 | * This exercises {@link NioChannelHub} differently because it has different channel 15 | * objects for read end and the write end. 16 | * 17 | * @author Kohsuke Kawaguchi 18 | */ 19 | public class NioPipeRunner extends AbstractNioChannelRunner { 20 | @Override 21 | public Channel start() throws Exception { 22 | final SynchronousQueue southHandoff = new SynchronousQueue<>(); 23 | 24 | final Pipe n2s = Pipe.open(); 25 | final Pipe s2n = Pipe.open(); 26 | 27 | nio = new NioChannelHub(executor); 28 | nio.setFrameSize(132); // force unaligned boundaries to shake things up a bit 29 | 30 | executor.submit(() -> { 31 | try { 32 | nio.run(); 33 | } catch (Throwable e) { 34 | LOGGER.log(Level.WARNING, "Faield to keep the NIO selector thread going", e); 35 | failure = e; 36 | } 37 | }); 38 | executor.submit(() -> { 39 | try { 40 | Channel south = nio.newChannelBuilder("south", executor) 41 | .withMode(Channel.Mode.NEGOTIATE) 42 | .build(n2s.source(), s2n.sink()); 43 | southHandoff.put(south); 44 | south.join(); 45 | System.out.println("south completed"); 46 | } catch (Exception e) { 47 | e.printStackTrace(); 48 | failure = e; 49 | } 50 | }); 51 | 52 | // create a client channel that connects to the same hub 53 | Channel north = nio.newChannelBuilder("north", executor) 54 | .withMode(Channel.Mode.BINARY) 55 | .build(s2n.source(), n2s.sink()); 56 | south = southHandoff.poll(10, TimeUnit.SECONDS); 57 | return north; 58 | } 59 | 60 | @Override 61 | public String getName() { 62 | return "NIO+pipe"; 63 | } 64 | 65 | private static final Logger LOGGER = Logger.getLogger(NioSocketRunner.class.getName()); 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/PipeWriterTestChecker.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | /** 4 | * @author Kohsuke Kawaguchi 5 | */ 6 | public interface PipeWriterTestChecker { 7 | void assertSlowStreamNotTouched(); 8 | 9 | void assertSlowStreamTouched(); 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/PrefetchTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import static org.junit.jupiter.api.Assertions.assertFalse; 27 | import static org.junit.jupiter.api.Assertions.assertTrue; 28 | 29 | import java.io.IOException; 30 | import org.junit.jupiter.params.ParameterizedTest; 31 | import org.junit.jupiter.params.provider.MethodSource; 32 | import org.objectweb.asm.ClassReader; 33 | 34 | /** 35 | * @author Kohsuke Kawaguchi 36 | */ 37 | public class PrefetchTest { 38 | @ParameterizedTest 39 | @MethodSource(ChannelRunners.PROVIDER_METHOD) 40 | public void testPrefetch(ChannelRunner channelRunner) throws Exception { 41 | channelRunner.withChannel(channel -> { 42 | VerifyTask vt = new VerifyTask(); 43 | assertTrue(channel.preloadJar(vt, ClassReader.class)); 44 | assertFalse(channel.preloadJar(vt, ClassReader.class)); 45 | // TODO: how can I do a meaningful test of this feature? 46 | System.out.println(channel.call(vt)); 47 | }); 48 | } 49 | 50 | private static class VerifyTask extends CallableBase { 51 | @Override 52 | public String call() throws IOException { 53 | return "verified"; 54 | } 55 | 56 | private static final long serialVersionUID = 1L; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/TestLinkage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2013 Jesse Glick. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package hudson.remoting; 26 | 27 | public class TestLinkage extends CallableBase { 28 | 29 | @Override 30 | public Object call() { 31 | // force classloading of several classes 32 | // when we run this test, we insert an artificial delay to each classloading 33 | // so that we can intercept the classloading. 34 | return A.field; 35 | } 36 | 37 | public static class A { 38 | public static Object field = new B().toString(); 39 | } 40 | 41 | public static class B {} 42 | 43 | private static final long serialVersionUID = 1L; 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/TestStaticGetResources.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.IOException; 4 | 5 | public class TestStaticGetResources extends CallableBase { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | private static boolean FIRST_RESOURCE; 10 | 11 | static { 12 | try { 13 | FIRST_RESOURCE = TestStaticGetResources.class 14 | .getClassLoader() 15 | .getResources("BLAH") 16 | .hasMoreElements(); 17 | } catch (IOException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | 22 | @Override 23 | public Object call() { 24 | return "found the impossible: " + FIRST_RESOURCE; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/TestStaticResourceReference.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | public class TestStaticResourceReference extends CallableBase { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | // this is really just to check that we can initialize a static property from searching a classpath resource 8 | private static boolean FALSE = 9 | TestStaticResourceReference.class.getClassLoader().getResource("BLAH") != null; 10 | 11 | @Override 12 | public Object call() { 13 | return "found the impossible: " + FALSE; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/TrafficAnalyzer.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.DataInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.ObjectInputStream; 7 | 8 | /** 9 | * A little forensic analysis tool to figure out what information controller and agent are exchanging. 10 | * 11 | *

12 | * Use the tee command or network packet capturing tool to capture the traffic between the controller and 13 | * the agent, then run it through this tool to get the dump of what commands are sent between them. 14 | * 15 | * @author Kohsuke Kawaguchi 16 | */ 17 | public class TrafficAnalyzer { 18 | public static void main(String[] args) throws Exception { 19 | File f = new File("/home/kohsuke/ws/hudson/investigations/javafx-windows-hang/out.log"); 20 | try (DataInputStream fin = new DataInputStream(new FileInputStream(f))) { 21 | fin.readFully(new byte[4]); // skip preamble 22 | try (ObjectInputStream ois = new ObjectInputStream(fin)) { 23 | for (int n = 0; ; n++) { 24 | Command o = (Command) ois.readObject(); 25 | System.out.println("#" + n + " : " + o); 26 | if (o instanceof RemoteInvocationHandler.RPCRequest) { 27 | RemoteInvocationHandler.RPCRequest request = (RemoteInvocationHandler.RPCRequest) o; 28 | System.out.print(" ("); 29 | boolean first = true; 30 | for (Object argument : request.getArguments()) { 31 | if (first) { 32 | first = false; 33 | } else { 34 | System.out.print(","); 35 | } 36 | System.out.print(argument); 37 | } 38 | System.out.println(")"); 39 | } 40 | if (o.createdAt != null) { 41 | o.createdAt.printStackTrace(System.out); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/URLDeserializatinHelperTest.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting; 2 | 3 | import java.io.IOException; 4 | import java.net.Proxy; 5 | import java.net.URL; 6 | import org.junit.Test; 7 | 8 | public class URLDeserializatinHelperTest { 9 | @Test 10 | public void openURLWithProxy() throws IOException { 11 | URL original = new URL("https://localhost"); 12 | URL url = URLDeserializationHelper.wrapIfRequired(original); 13 | url.openConnection(Proxy.NO_PROXY); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/WithRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2010, InfraDNA, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package hudson.remoting; 25 | 26 | import java.lang.annotation.Documented; 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Inherited; 29 | import java.lang.annotation.Retention; 30 | import java.lang.annotation.RetentionPolicy; 31 | import java.lang.annotation.Target; 32 | 33 | /** 34 | * Specify the channel runners for a test case. 35 | * @author Kohsuke Kawaguchi 36 | */ 37 | @Retention(RetentionPolicy.RUNTIME) 38 | @Target(ElementType.TYPE) 39 | @Documented 40 | @Inherited 41 | public @interface WithRunner { 42 | Class[] value(); 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/pipe/RandomWorkload.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.pipe; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.util.Random; 7 | import org.junit.Assert; 8 | 9 | /** 10 | * Use {@link Random} with the fixed seed as the data stream to detect corruption. 11 | * 12 | * To shake things up a bit, reading and writing at the different byte[] size boundary. 13 | * 14 | * @author Kohsuke Kawaguchi 15 | */ 16 | public class RandomWorkload extends Assert implements Workload { 17 | private final long size; 18 | 19 | /** 20 | * Size of the test. 21 | */ 22 | public RandomWorkload(long size) { 23 | this.size = size; 24 | } 25 | 26 | @Override 27 | public void write(OutputStream o) throws IOException { 28 | Random data = new Random(0); 29 | Random boundary = new Random(1); 30 | 31 | for (long l = 0; l < size; ) { 32 | int c = boundary.nextInt(4096); 33 | c = (int) Math.min(c, size - l); 34 | 35 | byte[] buf = new byte[c]; 36 | for (int i = 0; i < c; i++) { 37 | buf[i] = (byte) data.nextInt(); 38 | } 39 | 40 | o.write(buf); 41 | l += c; 42 | } 43 | 44 | o.close(); 45 | } 46 | 47 | @Override 48 | public void read(InputStream i) throws IOException { 49 | Random data = new Random(0); 50 | Random boundary = new Random(2); 51 | 52 | long total = 0; 53 | while (true) { 54 | int c = boundary.nextInt(4096); 55 | byte[] buf = new byte[c]; 56 | 57 | int n = i.read(buf); 58 | if (n < 0) { 59 | break; 60 | } 61 | 62 | for (int j = 0; j < n; j++) { 63 | assertEquals(buf[j], (byte) data.nextInt()); 64 | } 65 | 66 | total += n; 67 | } 68 | 69 | i.close(); 70 | assertEquals(size, total); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/pipe/Workload.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.pipe; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | /** 8 | * Abstraction for creating and verifying data that goes over a input/output stream pair. 9 | * 10 | * @author Kohsuke Kawaguchi 11 | */ 12 | public interface Workload { 13 | void write(OutputStream o) throws IOException; 14 | 15 | void read(InputStream i) throws IOException; 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/throughput/DumbReceiver.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.throughput; 2 | 3 | import java.io.OutputStream; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import org.apache.commons.io.IOUtils; 7 | 8 | /** 9 | * @author Kohsuke Kawaguchi 10 | */ 11 | public class DumbReceiver { 12 | public static void main(String[] args) throws Exception { 13 | ServerSocket ss = new ServerSocket(PORT); 14 | while (true) { 15 | System.out.println("Ready"); 16 | try (Socket s = ss.accept()) { 17 | System.out.println("Accepted"); 18 | IOUtils.copy(s.getInputStream(), OutputStream.nullOutputStream()); 19 | } 20 | } 21 | } 22 | 23 | public static final int PORT = 9533; 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/throughput/DumbSender.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.throughput; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.net.Socket; 5 | import java.util.Random; 6 | import java.util.concurrent.TimeUnit; 7 | import org.apache.commons.io.IOUtils; 8 | 9 | /** 10 | * @author Kohsuke Kawaguchi 11 | */ 12 | public class DumbSender { 13 | public static void main(String[] args) throws Exception { 14 | byte[] payload = getRandomSequence(); 15 | 16 | for (int i = 0; i < 2; i++) { 17 | try (Socket s = new Socket("127.0.0.2", DumbReceiver.PORT)) { 18 | System.out.println("Started"); 19 | long start = System.nanoTime(); 20 | IOUtils.copy(new ByteArrayInputStream(payload), s.getOutputStream()); 21 | System.out.println("Done: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); 22 | } 23 | } 24 | } 25 | 26 | private static byte[] getRandomSequence() { 27 | byte[] buf = new byte[10 * 1024 * 1024]; 28 | new Random(0).nextBytes(buf); 29 | return buf; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/throughput/Receiver.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.throughput; 2 | 3 | import hudson.remoting.Channel; 4 | import hudson.remoting.ChannelBuilder; 5 | import hudson.remoting.SocketChannelStream; 6 | import java.io.BufferedInputStream; 7 | import java.io.BufferedOutputStream; 8 | import java.net.ServerSocket; 9 | import java.net.Socket; 10 | import java.util.concurrent.Executors; 11 | 12 | /** 13 | * Accepts a channel one at a time. 14 | * 15 | * @author Kohsuke Kawaguchi 16 | */ 17 | public class Receiver { 18 | public static void main(String[] args) throws Exception { 19 | try (ServerSocket ss = new ServerSocket(PORT)) { 20 | while (true) { 21 | System.out.println("Ready"); 22 | try (Socket s = ss.accept()) { 23 | s.setTcpNoDelay(true); 24 | System.out.println("Accepted"); 25 | Channel ch = new ChannelBuilder("bogus", Executors.newCachedThreadPool()) 26 | .build( 27 | new BufferedInputStream(SocketChannelStream.in(s)), 28 | new BufferedOutputStream(SocketChannelStream.out(s))); 29 | ch.join(); 30 | } 31 | } 32 | } 33 | } 34 | 35 | public static final int PORT = 9532; 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/hudson/remoting/util/GCTask.java: -------------------------------------------------------------------------------- 1 | package hudson.remoting.util; 2 | 3 | import hudson.remoting.CallableBase; 4 | import java.io.IOException; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * @author Kohsuke Kawaguchi 10 | */ 11 | public class GCTask extends CallableBase { 12 | private final boolean agressive; 13 | 14 | public GCTask() { 15 | this(false); 16 | } 17 | 18 | public GCTask(boolean agressive) { 19 | this.agressive = agressive; 20 | } 21 | 22 | @Override 23 | public Object call() throws IOException { 24 | if (agressive) { 25 | Set objects = new HashSet<>(); 26 | int size = ((int) Math.min(Runtime.getRuntime().freeMemory(), Integer.MAX_VALUE)) / 32; 27 | while (true) { 28 | try { 29 | objects.add(new Object[size]); 30 | } catch (OutOfMemoryError ignore) { 31 | break; 32 | } 33 | } 34 | } 35 | System.gc(); 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/engine/JnlpAgentEndpointResolverTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2024 CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.jenkinsci.remoting.engine; 26 | 27 | import java.net.Inet6Address; 28 | import org.junit.Test; 29 | 30 | public final class JnlpAgentEndpointResolverTest { 31 | 32 | /** @see Inet6Address */ 33 | @Test 34 | public void getResolvedHttpProxyAddressIPv6() throws Exception { 35 | JnlpAgentEndpointResolver.getResolvedHttpProxyAddress("localhost", 12345); 36 | JnlpAgentEndpointResolver.getResolvedHttpProxyAddress("127.0.0.1", 12345); 37 | // Ignore return value, just assert that it does not throw an exception: 38 | JnlpAgentEndpointResolver.getResolvedHttpProxyAddress("0:0:0:0:0:0:0:1%lo", 12345); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/engine/PropertiesStringMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2004-2015, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.engine; 25 | 26 | import org.mockito.ArgumentMatcher; 27 | 28 | /** 29 | * Matcher that allows comparing Properties.store() results that may differ 30 | * only in the date comment line. 31 | * 32 | * @author Akshay Dayal 33 | */ 34 | public class PropertiesStringMatcher implements ArgumentMatcher { 35 | private String expected; 36 | 37 | public PropertiesStringMatcher(String expected) { 38 | this.expected = expected.substring(expected.indexOf(System.lineSeparator())); 39 | } 40 | 41 | @Override 42 | public boolean matches(String argument) { 43 | return argument.endsWith(expected); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/engine/WorkDirManagerRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2017 CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.engine; 25 | 26 | import org.junit.rules.ExternalResource; 27 | import org.junit.runner.Description; 28 | import org.junit.runners.model.Statement; 29 | 30 | /** 31 | * Contains automatic state reset for {@link WorkDirManager}. 32 | * @author Oleg Nenashev 33 | */ 34 | public class WorkDirManagerRule extends ExternalResource { 35 | 36 | WorkDirManager instance; 37 | 38 | public WorkDirManager getInstance() { 39 | return instance; 40 | } 41 | 42 | @Override 43 | public Statement apply(Statement base, Description description) { 44 | instance = WorkDirManager.getInstance(); 45 | return super.apply(base, description); 46 | } 47 | 48 | @Override 49 | protected void after() { 50 | WorkDirManager.reset(); 51 | } 52 | 53 | @Override 54 | protected void before() { 55 | WorkDirManager.reset(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/nio/FlushEveryByteStream.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.nio; 2 | 3 | import java.io.FilterOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | 7 | /** 8 | * @author Kohsuke Kawaguchi 9 | */ 10 | public class FlushEveryByteStream extends FilterOutputStream { 11 | public FlushEveryByteStream(OutputStream out) { 12 | super(out); 13 | } 14 | 15 | @Override 16 | public void write(int b) throws IOException { 17 | super.write(b); 18 | flush(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/nio/SocketClientMain.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.nio; 2 | 3 | import hudson.remoting.CallableBase; 4 | import hudson.remoting.Channel; 5 | import hudson.remoting.ChannelBuilder; 6 | import java.net.Socket; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | import java.util.logging.Logger; 10 | 11 | /** 12 | * @author Kohsuke Kawaguchi 13 | */ 14 | public class SocketClientMain { 15 | public static void main(String[] args) throws Exception { 16 | final ExecutorService es = Executors.newCachedThreadPool(); 17 | Socket s = new Socket("localhost", 9953); 18 | LOGGER.info("Cnonected"); 19 | Channel ch = new ChannelBuilder("client", es) 20 | .withHeaderStream(new FlushEveryByteStream(System.out)) 21 | .withMode(Channel.Mode.BINARY) 22 | .build(s); 23 | LOGGER.info("Established."); 24 | 25 | LOGGER.info("Got " + echo(ch, "Hello!")); 26 | 27 | ch.close(); 28 | ch.join(); 29 | es.shutdown(); 30 | } 31 | 32 | private static String echo(Channel ch, final String arg) throws Exception { 33 | return ch.call(new EchoingCallable(arg)); 34 | } 35 | 36 | private static final Logger LOGGER = Logger.getLogger(SocketClientMain.class.getName()); 37 | 38 | private static class EchoingCallable extends CallableBase { 39 | private final String arg; 40 | 41 | public EchoingCallable(String arg) { 42 | this.arg = arg; 43 | } 44 | 45 | @Override 46 | public String call() throws Exception { 47 | LOGGER.info("Echoing back " + arg); 48 | return arg; 49 | } 50 | 51 | private static final long serialVersionUID = 1L; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/nio/SocketServerMain.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.nio; 2 | 3 | import hudson.remoting.Channel; 4 | import java.io.IOException; 5 | import java.net.InetSocketAddress; 6 | import java.net.Socket; 7 | import java.nio.channels.SelectionKey; 8 | import java.nio.channels.ServerSocketChannel; 9 | import java.nio.channels.SocketChannel; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.logging.Level; 13 | import java.util.logging.Logger; 14 | 15 | /** 16 | * @author Kohsuke Kawaguchi 17 | */ 18 | public class SocketServerMain { 19 | public static void main(String[] args) throws Exception { 20 | final ExecutorService es = Executors.newCachedThreadPool(); 21 | 22 | ServerSocketChannel ss = ServerSocketChannel.open(); 23 | ss.configureBlocking(false); 24 | ss.socket().bind(new InetSocketAddress(9953)); 25 | 26 | NioChannelHub nio = new NioChannelHub(es) { 27 | @Override 28 | protected void onSelected(SelectionKey key) { 29 | try { 30 | ServerSocketChannel ss = (ServerSocketChannel) key.channel(); 31 | LOGGER.info("Acccepted"); 32 | final SocketChannel con = ss.accept(); 33 | es.submit(() -> { 34 | try { 35 | // TODO: this is where we do more config 36 | Socket socket = con.socket(); 37 | // TODO: does this actually produce async channel? 38 | Channel ch = newChannelBuilder(con.toString(), es) 39 | .withHeaderStream(new FlushEveryByteStream(System.out)) 40 | .build(socket); 41 | LOGGER.info("Connected to " + ch); 42 | } catch (IOException e) { 43 | LOGGER.log(Level.WARNING, "Handshake failed", e); 44 | } 45 | }); 46 | } catch (IOException e) { 47 | LOGGER.log(Level.WARNING, "Failed to accept a soccket", e); 48 | } 49 | } 50 | }; 51 | ss.register(nio.getSelector(), SelectionKey.OP_ACCEPT); 52 | LOGGER.info("Waiting for connection"); 53 | nio.run(); 54 | } 55 | 56 | private static final Logger LOGGER = Logger.getLogger(SocketServerMain.class.getName()); 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/NetworkLayerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol; 25 | 26 | import java.nio.channels.ReadableByteChannel; 27 | import java.nio.channels.WritableByteChannel; 28 | import org.jenkinsci.remoting.protocol.impl.BIONetworkLayer; 29 | import org.jenkinsci.remoting.protocol.impl.NIONetworkLayer; 30 | 31 | public interface NetworkLayerFactory { 32 | 33 | NetworkLayerFactory[] ALL = new NetworkLayerFactory[] {new NIO(), new BIO()}; 34 | 35 | NetworkLayer create(IOHub selector, ReadableByteChannel in, WritableByteChannel out); 36 | 37 | class NIO implements NetworkLayerFactory { 38 | 39 | @Override 40 | public NetworkLayer create(IOHub selector, ReadableByteChannel in, WritableByteChannel out) { 41 | return new NIONetworkLayer(selector, in, out); 42 | } 43 | } 44 | 45 | class BIO implements NetworkLayerFactory { 46 | 47 | @Override 48 | public NetworkLayer create(IOHub selector, ReadableByteChannel in, WritableByteChannel out) { 49 | return new BIONetworkLayer(selector, in, out); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/Repeat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol; 25 | 26 | import java.lang.annotation.ElementType; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | import java.util.concurrent.TimeUnit; 31 | 32 | @Retention(RetentionPolicy.RUNTIME) 33 | @Target({ElementType.METHOD, ElementType.TYPE}) 34 | public @interface Repeat { 35 | int value() default 0; 36 | 37 | long stopAfter() default 0L; 38 | 39 | TimeUnit stopAfterUnits() default TimeUnit.SECONDS; 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/cert/DHKeyPairRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol.cert; 25 | 26 | import java.security.KeyPair; 27 | import java.security.KeyPairGenerator; 28 | import java.security.NoSuchAlgorithmException; 29 | import javax.crypto.interfaces.DHPrivateKey; 30 | import javax.crypto.interfaces.DHPublicKey; 31 | 32 | public class DHKeyPairRule extends KeyPairRule { 33 | 34 | public DHKeyPairRule() { 35 | super(""); 36 | } 37 | 38 | public DHKeyPairRule(String id) { 39 | super(id); 40 | } 41 | 42 | @Override 43 | protected KeyPair generateKeyPair() throws NoSuchAlgorithmException { 44 | KeyPairGenerator gen = KeyPairGenerator.getInstance("DH"); 45 | gen.initialize(1024); // maximum supported by JVM with export restrictions 46 | return gen.generateKeyPair(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/cert/DSAKeyPairRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol.cert; 25 | 26 | import java.security.KeyPair; 27 | import java.security.KeyPairGenerator; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.interfaces.DSAPrivateKey; 30 | import java.security.interfaces.DSAPublicKey; 31 | 32 | public class DSAKeyPairRule extends KeyPairRule { 33 | 34 | public DSAKeyPairRule() { 35 | super(""); 36 | } 37 | 38 | public DSAKeyPairRule(String id) { 39 | super(id); 40 | } 41 | 42 | @Override 43 | protected KeyPair generateKeyPair() throws NoSuchAlgorithmException { 44 | KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA"); 45 | gen.initialize(1024); // maximum supported by JVM with export restrictions 46 | return gen.generateKeyPair(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/cert/ECKeyPairRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol.cert; 25 | 26 | import java.security.KeyPair; 27 | import java.security.KeyPairGenerator; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.interfaces.ECPrivateKey; 30 | import java.security.interfaces.ECPublicKey; 31 | 32 | public class ECKeyPairRule extends KeyPairRule { 33 | 34 | public ECKeyPairRule() { 35 | super(""); 36 | } 37 | 38 | public ECKeyPairRule(String id) { 39 | super(id); 40 | } 41 | 42 | @Override 43 | protected KeyPair generateKeyPair() throws NoSuchAlgorithmException { 44 | KeyPairGenerator gen = KeyPairGenerator.getInstance("EC"); 45 | gen.initialize(256); // this is the default keysize supported by SunEC 46 | return gen.generateKeyPair(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/cert/RSAKeyPairRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol.cert; 25 | 26 | import java.security.KeyPair; 27 | import java.security.KeyPairGenerator; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.interfaces.RSAPrivateKey; 30 | import java.security.interfaces.RSAPublicKey; 31 | 32 | public class RSAKeyPairRule extends KeyPairRule { 33 | 34 | public RSAKeyPairRule() { 35 | super(""); 36 | } 37 | 38 | public RSAKeyPairRule(String id) { 39 | super(id); 40 | } 41 | 42 | @Override 43 | protected KeyPair generateKeyPair() throws NoSuchAlgorithmException { 44 | KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); 45 | gen.initialize(2048); // maximum supported by JVM with export restrictions 46 | return gen.generateKeyPair(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/protocol/impl/NoOpFilterLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2016, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.remoting.protocol.impl; 25 | 26 | import edu.umd.cs.findbugs.annotations.NonNull; 27 | import java.io.IOException; 28 | import java.nio.ByteBuffer; 29 | import org.jenkinsci.remoting.protocol.FilterLayer; 30 | 31 | public class NoOpFilterLayer extends FilterLayer { 32 | @Override 33 | public void onRecv(@NonNull ByteBuffer data) throws IOException { 34 | next().onRecv(data); 35 | } 36 | 37 | @Override 38 | public void doSend(@NonNull ByteBuffer data) throws IOException { 39 | next().doSend(data); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/util/DurationFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.time.Duration; 6 | import org.junit.Test; 7 | 8 | public class DurationFormatterTest { 9 | @Test 10 | public void typical() { 11 | assertEquals("1 second", DurationFormatter.format(Duration.ofSeconds(1))); 12 | assertEquals("2 seconds", DurationFormatter.format(Duration.ofSeconds(2))); 13 | assertEquals( 14 | "1 day, 2 seconds", DurationFormatter.format(Duration.ofDays(1).plus(Duration.ofSeconds(2)))); 15 | assertEquals( 16 | "2 days, 3 hours, 2 seconds", 17 | DurationFormatter.format( 18 | Duration.ofDays(2).plus(Duration.ofHours(3)).plus(Duration.ofSeconds(2)))); 19 | assertEquals( 20 | "2 days, 3 hours, 1 minute, 2 seconds", 21 | DurationFormatter.format(Duration.ofDays(2) 22 | .plus(Duration.ofHours(3)) 23 | .plus(Duration.ofMinutes(1)) 24 | .plus(Duration.ofSeconds(2)))); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/remoting/util/DurationStyleTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.remoting.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.time.Duration; 6 | import org.junit.Test; 7 | 8 | public class DurationStyleTest { 9 | @Test 10 | public void typical() { 11 | assertEquals(Duration.ofSeconds(1), DurationStyle.detectAndParse("1s")); 12 | assertEquals(Duration.ofMinutes(2), DurationStyle.detectAndParse("2m")); 13 | assertEquals(Duration.ofHours(3), DurationStyle.detectAndParse("3h")); 14 | assertEquals(Duration.ofDays(4), DurationStyle.detectAndParse("4d")); 15 | } 16 | 17 | @Test 18 | public void negative() { 19 | assertEquals(Duration.ofSeconds(1).negated(), DurationStyle.detectAndParse("-1s")); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/hudson/remoting/embedded_doctype.jnlp: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | ]> 6 | 7 | 8 | Agent for local 9 | Jenkins project 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | secret_key 21 | local 22 | -workDir 23 | /jenkins/nodes/local 24 | -internalDir 25 | remoting 26 | -url 27 | http://jenkins:8080/ 28 | 29 | -------------------------------------------------------------------------------- /src/test/resources/hudson/remoting/lol.jnlp: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ]> 15 | &lol9; 16 | 17 | 18 | Agent for local 19 | Jenkins project 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | secret_key 31 | local 32 | -workDir 33 | /jenkins/nodes/local 34 | -internalDir 35 | remoting 36 | -url 37 | http://jenkins:8080/ 38 | 39 | -------------------------------------------------------------------------------- /src/test/resources/hudson/remoting/note.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/test/resources/hudson/remoting/test.jnlp: -------------------------------------------------------------------------------- 1 | 2 | 3 | Agent for local 4 | Jenkins project 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | secret_key 16 | local 17 | -workDir 18 | /jenkins/nodes/local 19 | -internalDir 20 | remoting 21 | -url 22 | http://jenkins:8080/ 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/hudson/remoting/xxe_file.jnlp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Agent for local 6 | Jenkins project 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | secret_key 18 | local 19 | -workDir 20 | /jenkins/nodes/local 21 | -internalDir 22 | remoting 23 | -url 24 | http://jenkins:8080/ 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/test/resources/hudson/remoting/xxe_http.jnlp: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | ]> 7 | 8 | 9 | Agent for local 10 | Jenkins project 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | secret_key 22 | local 23 | -workDir 24 | /jenkins/nodes/local 25 | -internalDir 26 | remoting 27 | -url 28 | http://jenkins:8080/ 29 | 30 | --------------------------------------------------------------------------------