Skip to content

[JEP-222] Add experimental WebSocket support + Jetty update in Winstone + Remoting 4.0 - #1

Open
MarckDuan wants to merge 298 commits into
masterfrom
websocket
Open

[JEP-222] Add experimental WebSocket support + Jetty update in Winstone + Remoting 4.0#1
MarckDuan wants to merge 298 commits into
masterfrom
websocket

Conversation

@MarckDuan

Copy link
Copy Markdown

See JEP-222. Downstream of jenkinsci/winstone#79 + jenkinsci/remoting#357 + jenkinsci/jenkins-test-harness#183.

Proposed Changelog Entries

oleg-nenashev and others added 30 commits November 12, 2019 15:06
[JENKINS-59631] Fix styling issue in build history table
* Try to execute flyweight tasks on the master

* Fix offline behavior, add tests

* Remove obsolete TODOs, also check #isAcceptingTasks
[JENKINS-59793] Avoid hanging jobs with faulty SubTasks
The jenkins-slave.xml file has been copied to the windows-slave-installer-module,
but a unused (old) copy is still present in the jenkins core.
…pane of the Plugin Manager (#4298)

* refs #JENKINS-59665 Fixed sorting for uninstall column of installed in plugin manager

* refs #JENKINS-59665 removed test attribute
* Hide password form fields by default

* Trying to bypass enforced autocompletion by just having a test field at first

Something like onfocus didn't work, you'd tab through form elements
and unless you filled in the user name, changing the form field to
password would cause it to autocomplete.

It looks like, at least in Mac/Firefox, going from plain text to password
in the 'oninput' event handler works. The plain text is revealed neither
with typing nor pasting.

* Update core/src/main/resources/lib/form/password.jelly

Co-Authored-By: daniel-beck <daniel-beck@users.noreply.github.com>

* Use previously defined value

* Make new password form fields opt-out

* Add support for redacting form secrets in new password field

* Have a password value pre-set in Jelly

* Fix method

* Fix test by clicking on the button to change the password

* Forgot period separator between class and property name
[JENKINS-59508] Sidebar links show full label on hover
Switch from maven-jenkins-dev-plugin to upstream jetty-maven-plugin
Add StandardOpenOption.CREATE flag to create FileChannelWriter to avoid full fs flush and 5sec log operation on creating empty file with CephFS as a storage
* JENKINS-6722 Show slave error status on Dashboard

* Update executors.properties
…unds on the plugin side (#3935)

* JENKINS-56553 Make proxy config compatible with JCASC

* Use DataBoundSetters as other fields are optional

* Fix Jelly for ProxyConfiguration

* Use default field name now that field matches

* Fix validate proxy

* Change field type

* After review

* Update core/src/main/java/jenkins/model/Jenkins.java

Co-Authored-By: timja <t.jacomb@kainos.com>

* Update core/src/main/java/jenkins/model/Jenkins.java

Co-Authored-By: timja <t.jacomb@kainos.com>

* Update core/src/main/java/hudson/ProxyConfiguration.java

Co-Authored-By: timja <t.jacomb@kainos.com>

* Update core/src/main/java/jenkins/model/Jenkins.java

Co-Authored-By: timja <t.jacomb@kainos.com>

* Update core/src/main/java/jenkins/model/Jenkins.java

Co-Authored-By: timja <t.jacomb@kainos.com>

* Update core/src/main/java/hudson/ProxyConfiguration.java

Co-Authored-By: Oleg Nenashev <o.v.nenashev@gmail.com>

* Tweak javadoc

* Align fields

* Update core/src/main/java/hudson/ProxyConfiguration.java

Co-Authored-By: Adrien Lecharpentier <adrien.lecharpentier@gmail.com>
Document workaround for Stapler data binding bug in repeatableProperty
…ling events (#4345)

* Rejuvenate log levels

* clean up

* Reverting the plugin download

Co-Authored-By: Oleg Nenashev <o.v.nenashev@gmail.com>
oleg-nenashev and others added 27 commits January 10, 2020 14:46
Inlining test-pom into test and removing test-jdk8
…IZABLE, ES_COMPARING_STRINGS_WITH_EQ) (#4379)

* fixed Spotbugs issues (SE_COMPARATOR_SHOULD_BE_SERIALIZABLE, ES_COMPARING_STRINGS_WITH_EQ)

* replaced wildcard import with specific imports

* added missing import

* fixed spotbugs issues

* removed accidently added import
…stages (#4207)

* [JENKINS-59412] Clarify that build history does not include pipeline stages

* Clarify that pipelines are merely an example of tasks that may not be listed
Simplify CoreUpdateMonitor/message.properties: changelog.url → jenkins-version.properties
* Add the maintainer checklist to the pull request template

* Add checklist items for LTS

* Add "Proposed changelog entries" to the pull request template

* Pull request template: `Internal: ` prefix is now added automatically by the Core Changelog generator

jenkinsci/core-changelog-generator#6

* Apply suggestions from review by @MarkEWaite

Co-Authored-By: Mark Waite <mark.earl.waite@gmail.com>

Co-authored-by: Mark Waite <mark.earl.waite@gmail.com>
Upload draft Changelog YAMLs to GitHub Release assets
…or-yaml

Revert "Upload draft Changelog YAMLs to GitHub Release assets"
[JENKINS-60740] - Rely on GitHub Actions to generate the release draft YAMLs
Release Drafter Fix: Properly inherit the parent config
[JENKINS-60716] Retain causes of a LogRotator failure for diagnosis
* Spotbugs: Fixed comparing strings with equals instead of ==

* Spotbugs: Do not return null in @nonnull marked method, instead return empty string.

* Added Suppress for Spotbugs

* Apply suggestions from code review

Co-Authored-By: Oleg Nenashev <o.v.nenashev@gmail.com>

* fixed javadoc

Co-authored-by: Oleg Nenashev <o.v.nenashev@gmail.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

JEP-222: Experimental WebSocket agent transport + Remoting 4.0 + Winstone/Jetty update

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Adds experimental WebSocket transport for inbound agents over HTTP(S) (JEP-222).
• Adds CLI WebSocket mode and refactors the plain CLI protocol for multiple transports.
• Bumps Remoting to 4.0 and updates Winstone/Jetty tooling for local dev.
Diagram

graph TD
  Agent["agent.jar Engine"] -->|"WS upgrade"| WSAgents["/wsagents"] --> WSSession["WebSocketSession"] --> WSBridge["WebSockets bridge"]
  WSSession --> Remoting["Remoting Channel"]
  CLIClient["CLI -webSocket"] -->|"WS"| CLIWs["/cli/ws"] --> PlainCLI["PlainCLIProtocol"]
  WSBridge -.->|"reflection"| Jetty[("Jetty WS API")]
  subgraph Legend
    direction LR
    _svc(["Endpoint"]) ~~~ _mod(["Module"]) ~~~ _ext[("External lib")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hard dependency on Jetty/JSR-356 WebSocket APIs
  • ➕ Compile-time type safety, simpler code
  • ➕ Avoids reflection and dynamic proxy handling
  • ➖ Tightly couples Jenkins core to a specific container/WebSocket stack
  • ➖ Risky for alternate servlet containers and version skew
2. Pluggable WebSocket SPI with container-specific implementations
  • ➕ Cleaner long-term abstraction; easy to add Tomcat/other providers
  • ➕ Minimizes reflection usage in core logic
  • ➖ More upfront design and maintenance, heavier for an experimental feature
  • ➖ Requires packaging/bootstrapping an SPI across war/winstone deployments

Recommendation: Given Jenkins’ need to run in varied servlet-container contexts and the experimental scope, the reflection-based Jetty bridge is a pragmatic choice. If WebSocket transport graduates from experimental, consider evolving toward a small SPI/provider model to reduce reflective surface area and improve testability.

Files changed (30) +1377 / -675

Enhancement (8) +885 / -174
CLI.javaAdd -webSocket mode to CLI client +145/-58

Add -webSocket mode to CLI client

• Introduces WEB_SOCKET mode using a Tyrus client to connect to /cli/ws, sending PlainCLIProtocol frames as WS binary messages and handling auth headers.

cli/src/main/java/hudson/cli/CLI.java

CLIAction.javaAdd /cli/ws endpoint for CLI over WebSocket +178/-99

Add /cli/ws endpoint for CLI over WebSocket

• Adds a WebSocket endpoint backed by WebSocketSession and reuses a refactored ServerSideImpl to run PlainCLIProtocol over WS frames.

core/src/main/java/hudson/cli/CLIAction.java

JNLPLauncher.javaAdd WebSocket option for inbound agents +34/-17

Add WebSocket option for inbound agents

• Introduces a webSocket flag, UI validation (no tunnel in WS mode), and checks for WS support vs TCP agent port availability.

core/src/main/java/hudson/slaves/JNLPLauncher.java

WebSocketAgents.javaImplement inbound agent WebSocket endpoint (/wsagents) +191/-0

Implement inbound agent WebSocket endpoint (/wsagents)

• Adds an UnprotectedRootAction that authenticates inbound agents via headers and secrets, then upgrades to WS and bridges binary frames to Remoting transport.

core/src/main/java/jenkins/agents/WebSocketAgents.java

WebSockets.javaAdd WebSockets upgrade support using Jetty reflection +117/-0

Add WebSockets upgrade support using Jetty reflection

• Implements WebSocket upgrade/creator logic via reflection and dynamic proxies, avoiding compile-time Jetty dependency.

core/src/main/java/jenkins/websocket/WebSockets.java

WebSocketSession.javaIntroduce WebSocketSession base with keep-alive pings +150/-0

Introduce WebSocketSession base with keep-alive pings

• Adds a base session class handling Jetty listener callbacks reflectively, provides sendBinary/sendText, and sends periodic pings to keep connections alive.

core/src/main/java/jenkins/websocket/WebSocketSession.java

WebSocketEcho.javaAdd admin-only WebSocket echo endpoint +67/-0

Add admin-only WebSocket echo endpoint

• Provides a simple /wsecho endpoint for validating WS functionality (text echo + binary transform).

core/src/main/java/jenkins/websocket/WebSocketEcho.java

config.jellyExpose WebSocket option in JNLPLauncher UI +3/-0

Expose WebSocket option in JNLPLauncher UI

• Adds a checkbox entry for the new webSocket field.

core/src/main/resources/hudson/slaves/JNLPLauncher/config.jelly

Refactor (4) +130 / -345
PlainCLIProtocol.javaRefactor protocol framing to support multiple transports +123/-98

Refactor protocol framing to support multiple transports

• Extracts framing into FramedReader/FramedOutput and an Output abstraction, enabling use over both HTTP full-duplex streams and WebSocket messages.

cli/src/main/java/hudson/cli/PlainCLIProtocol.java

JnlpSlaveAgentProtocol.javaRetain SLAVE_SECRET as deprecated compatibility alias +7/-88

Retain SLAVE_SECRET as deprecated compatibility alias

• Removes legacy protocol implementation and keeps only SLAVE_SECRET delegating to JnlpAgentReceiver for plugin compatibility.

core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol.java

DeprecatedAgentProtocolMonitor.javaRemove deprecated agent protocol monitor +0/-94

Remove deprecated agent protocol monitor

• Deletes the AdministrativeMonitor warning about deprecated agent protocols, aligning with protocol removals.

core/src/main/java/jenkins/slaves/DeprecatedAgentProtocolMonitor.java

jenkins-slave.xmlRemove unused jenkins-slave.xml from core +0/-65

Remove unused jenkins-slave.xml from core

• Deletes an obsolete Windows service definition file that had been moved to a dedicated installer module.

core/src/main/resources/windows-service/jenkins-slave.xml

Tests (3) +38 / -13
HexDumpTest.javaStabilize HexDump test expectations +5/-1

Stabilize HexDump test expectations

• Minor updates to tests around CLI hexdump behavior/output.

cli/src/test/java/hudson/cli/HexDumpTest.java

PlainCLIProtocolTest.javaExtend PlainCLIProtocol tests for new framing logic +22/-6

Extend PlainCLIProtocol tests for new framing logic

• Adds/updates tests validating the refactored framing and error handling.

cli/src/test/java/hudson/cli/PlainCLIProtocolTest.java

PrivateKeyProviderTest.javaAdjust key provider tests after CLI option changes +11/-6

Adjust key provider tests after CLI option changes

• Updates tests to reflect updated key-loading behavior and flags.

cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java

Documentation (6) +34 / -14
PULL_REQUEST_TEMPLATE.mdUpdate PR template guidance +18/-2

Update PR template guidance

• Refines the PR template content and expectations for contributors.

.github/PULL_REQUEST_TEMPLATE.md

CONTRIBUTING.mdRefresh contributing instructions +8/-9

Refresh contributing instructions

• Updates contributor documentation, including build/run guidance and references.

CONTRIBUTING.md

pom.xmlFix servlet-api comment typos +2/-2

Fix servlet-api comment typos

• Corrects spelling/wording in BOM comments; no dependency behavior change.

bom/pom.xml

Messages.propertiesAdjust CLI client messages +1/-0

Adjust CLI client messages

• Updates a localized message string used by the CLI client.

cli/src/main/resources/hudson/cli/client/Messages.properties

Messages_da.propertiesUpdate Danish CLI client localization +1/-1

Update Danish CLI client localization

• Tweaks Danish translation strings for CLI client messages.

cli/src/main/resources/hudson/cli/client/Messages_da.properties

help-webSocket.htmlAdd help text for WebSocket mode +4/-0

Add help text for WebSocket mode

• Documents the WebSocket mode and links to JEP-222.

core/src/main/resources/hudson/slaves/JNLPLauncher/help-webSocket.html

Other (9) +290 / -129
release-drafter.ymlSimplify Release Drafter configuration +7/-32

Simplify Release Drafter configuration

• Adjusts release-drafter categories/rules, reducing previous configuration entries.

.github/release-drafter.yml

changelog.ymlUpdate changelog workflow +43/-0

Update changelog workflow

• Modifies the GitHub Actions workflow used to generate or validate changelog entries.

.github/workflows/changelog.yml

JenkinsfileAdjust CI pipeline steps +3/-4

Adjust CI pipeline steps

• Minor tweaks to Jenkins pipeline configuration.

Jenkinsfile

pom.xmlSwitch CLI to shaded jar and add WebSocket client deps +81/-41

Switch CLI to shaded jar and add WebSocket client deps

• Replaces assembly packaging with shade, adds Tyrus WS client as optional dependency, and adds JUnit Jupiter for tests.

cli/pom.xml

pom.xmlAdd XMLUnit test dependencies +12/-0

Add XMLUnit test dependencies

• Adds xmlunit-core and xmlunit-matchers in test scope for XML comparisons.

core/pom.xml

pom.xmlBump core revision and Remoting to 4.0; remove legacy modules/plugins +2/-27

Bump core revision and Remoting to 4.0; remove legacy modules/plugins

• Updates project revision to 2.215, bumps bundled Remoting to 4.0, and removes references to the legacy maven-jenkins-dev-plugin and some old modules/profiles.

pom.xml

WebSocketAgentsTest.javaAdd smoke test for WebSocket inbound agents +125/-0

Add smoke test for WebSocket inbound agents

• Launches agent.jar and validates connectivity and build execution when using WebSocket mode.

test/src/test/java/jenkins/agents/WebSocketAgentsTest.java

pom.xmlUpdate Winstone and switch dev plugin to jetty-maven-plugin +7/-25

Update Winstone and switch dev plugin to jetty-maven-plugin

• Bumps Winstone to 5.6 and configures standard jetty-maven-plugin for local runs; removes special CLI jar-with-dependencies handling.

war/pom.xml

web.xmlDisable HTTP TRACE via security constraint +10/-0

Disable HTTP TRACE via security constraint

• Adds a security-constraint blocking TRACE requests, matching Jetty defaults.

war/src/main/webapp/WEB-INF/web.xml

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. WebSocket receiver race 🐞 Bug ☼ Reliability
Description
WebSocketAgents.Session.binary/closed dereference receiver before Transport.setup assigns it, so
early frames or close events can throw NullPointerException and drop inbound agent connections.
Code

core/src/main/java/jenkins/agents/WebSocketAgents.java[R129-132]

+        protected void binary(byte[] payload, int offset, int len) {
+            LOGGER.finest(() -> "reading block of length " + len + " from " + agent);
+            if (offset == 0 && len == payload.length) {
+                receiver.handle(payload);
Evidence
opened() sets up the channel asynchronously, while binary/closed immediately call methods on
receiver which is only assigned in Transport.setup, creating a race that can NPE.

core/src/main/java/jenkins/agents/WebSocketAgents.java[117-145]
core/src/main/java/jenkins/agents/WebSocketAgents.java[154-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`WebSocketAgents.Session` uses `receiver.handle(...)` and `receiver.terminate(...)` even though `receiver` is only assigned later in `Transport.setup(...)` and channel setup is submitted asynchronously. This can throw `NullPointerException` and break inbound agent connections.

### Issue Context
`opened()` submits channel setup to `Computer.threadPoolForRemoting`, so WebSocket callbacks (`binary`/`closed`) may run before `Transport.setup` executes.

### Fix Focus Areas
- Ensure `receiver` is non-null before use, e.g.:
 - assign a buffering receiver immediately and drain once `setup` runs, OR
 - block `binary` handling until `setup` completes, OR
 - close the WebSocket with a clear reason if `receiver` is not ready.
- Make `closed(...)` null-safe and ensure state disconnect hooks still fire.

### Fix Focus Areas (code refs)
- core/src/main/java/jenkins/agents/WebSocketAgents.java[117-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. WS agent cookie missing 🐞 Bug ≡ Correctness
Description
WebSocketAgents generates and sends a JnlpConnectionState cookie header but never sets
JnlpConnectionState.COOKIE_KEY in the connection state properties, so DefaultJnlpSlaveReceiver
cannot store the cookie on the Channel and will reject a reconnect while an old channel is still
considered connected.
Code

core/src/main/java/jenkins/agents/WebSocketAgents.java[R91-95]

+        state.fireAfterProperties(ImmutableMap.of(
+            // TODO or just pass all request headers?
+            JnlpConnectionState.CLIENT_NAME_KEY, agent,
+            JnlpConnectionState.SECRET_KEY, secret
+        ));
Evidence
WebSocketAgents does not include COOKIE_KEY in the properties it fires, while
DefaultJnlpSlaveReceiver reads COOKIE_KEY to both compare against an existing channel and to persist
it onto the new channel builder.

core/src/main/java/jenkins/agents/WebSocketAgents.java[87-101]
core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java[122-160]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`WebSocketAgents` sends a generated cookie in the HTTP response header, but the `JnlpConnectionState` never receives this cookie as a property. `DefaultJnlpSlaveReceiver` relies on `event.getProperty(JnlpConnectionState.COOKIE_KEY)` to (a) store the cookie on the `Channel` and (b) detect “reconnected from current peer” vs. reject-as-already-connected.

### Issue Context
`state.fireAfterProperties(...)` currently only includes `CLIENT_NAME_KEY` and `SECRET_KEY`, so the cookie is always null to connection listeners.

### Fix Focus Areas
- Generate the cookie once and reuse the same value:
 - include `JnlpConnectionState.COOKIE_KEY` in the `fireAfterProperties` property map (or equivalent state property setter), and
 - continue to set the same cookie in the response header.
- (Optional but consistent) if reconnect semantics require it, accept an incoming cookie header on subsequent connect attempts and set it into the state properties.

### Fix Focus Areas (code refs)
- core/src/main/java/jenkins/agents/WebSocketAgents.java[87-102]
- core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java[122-160]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. CLI WS close NPE 🐞 Bug ☼ Reliability
Description
CLIAction.doWs returns from opened() on IOException without closing the WebSocket, leaving
connection null; later closed() unconditionally calls connection.handleClose(), which can throw
NullPointerException and obscure the original failure.
Code

core/src/main/java/hudson/cli/CLIAction.java[R139-143]

+                try {
+                    connection = new ServerSideImpl(new OutputImpl(), authentication);
+                } catch (IOException x) {
+                    error(x);
+                    return;
Evidence
The error path in opened() returns with connection unset, but closed() always dereferences it.

core/src/main/java/hudson/cli/CLIAction.java[117-174]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In `CLIAction.doWs`, `opened()` catches an `IOException`, logs via `error(x)`, and returns without initializing `connection` or closing the session. Subsequent `binary(...)`/`closed(...)` callbacks dereference `connection`.

### Issue Context
`connection` is a field inside the anonymous `WebSocketSession` and is only assigned in `opened()`.

### Fix Focus Areas
- If `ServerSideImpl` initialization fails, close the WebSocket session immediately.
- Make `binary(...)` and `closed(...)` null-safe (e.g., check `connection != null` before use).

### Fix Focus Areas (code refs)
- core/src/main/java/hudson/cli/CLIAction.java[117-174]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Proxy normalization lost 🐞 Bug ≡ Correctness
Description
ProxyConfiguration moved userName/noProxyHost to @DataBoundSetter but setters no longer apply
Util.fixEmptyAndTrim, so blank values remain non-null and can trigger proxy authentication attempts
or change no-proxy matching behavior compared to the constructor path.
Code

core/src/main/java/hudson/ProxyConfiguration.java[R236-239]

+    @DataBoundSetter
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
Evidence
The constructor normalizes empty strings to null, while the new setters do not; proxy auth is
enabled based on userName != null, so behavior changes when databinding provides "".

core/src/main/java/hudson/ProxyConfiguration.java[137-159]
core/src/main/java/hudson/ProxyConfiguration.java[226-244]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ProxyConfiguration` constructor normalizes inputs with `Util.fixEmptyAndTrim`, but the new `@DataBoundSetter` methods store raw strings. With databinding (UI/config XML), empty strings are common and will now remain non-null.

### Issue Context
`newAuthenticator()` enables proxy auth whenever `userName != null`, so an empty-string username can now cause proxy auth attempts that previously would not have happened.

### Fix Focus Areas
- Apply `Util.fixEmptyAndTrim` (or equivalent) in setters:
 - `setUserName`, `setNoProxyHost`, and likely `setTestUrl`.
- Consider reinitializing `authenticator` when username/password changes if required.

### Fix Focus Areas (code refs)
- core/src/main/java/hudson/ProxyConfiguration.java[137-159]
- core/src/main/java/hudson/ProxyConfiguration.java[226-244]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +129 to +132
protected void binary(byte[] payload, int offset, int len) {
LOGGER.finest(() -> "reading block of length " + len + " from " + agent);
if (offset == 0 && len == payload.length) {
receiver.handle(payload);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Websocket receiver race 🐞 Bug ☼ Reliability

WebSocketAgents.Session.binary/closed dereference receiver before Transport.setup assigns it, so
early frames or close events can throw NullPointerException and drop inbound agent connections.
Agent Prompt
### Issue description
`WebSocketAgents.Session` uses `receiver.handle(...)` and `receiver.terminate(...)` even though `receiver` is only assigned later in `Transport.setup(...)` and channel setup is submitted asynchronously. This can throw `NullPointerException` and break inbound agent connections.

### Issue Context
`opened()` submits channel setup to `Computer.threadPoolForRemoting`, so WebSocket callbacks (`binary`/`closed`) may run before `Transport.setup` executes.

### Fix Focus Areas
- Ensure `receiver` is non-null before use, e.g.:
  - assign a buffering receiver immediately and drain once `setup` runs, OR
  - block `binary` handling until `setup` completes, OR
  - close the WebSocket with a clear reason if `receiver` is not ready.
- Make `closed(...)` null-safe and ensure state disconnect hooks still fire.

### Fix Focus Areas (code refs)
- core/src/main/java/jenkins/agents/WebSocketAgents.java[117-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +91 to +95
state.fireAfterProperties(ImmutableMap.of(
// TODO or just pass all request headers?
JnlpConnectionState.CLIENT_NAME_KEY, agent,
JnlpConnectionState.SECRET_KEY, secret
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Ws agent cookie missing 🐞 Bug ≡ Correctness

WebSocketAgents generates and sends a JnlpConnectionState cookie header but never sets
JnlpConnectionState.COOKIE_KEY in the connection state properties, so DefaultJnlpSlaveReceiver
cannot store the cookie on the Channel and will reject a reconnect while an old channel is still
considered connected.
Agent Prompt
### Issue description
`WebSocketAgents` sends a generated cookie in the HTTP response header, but the `JnlpConnectionState` never receives this cookie as a property. `DefaultJnlpSlaveReceiver` relies on `event.getProperty(JnlpConnectionState.COOKIE_KEY)` to (a) store the cookie on the `Channel` and (b) detect “reconnected from current peer” vs. reject-as-already-connected.

### Issue Context
`state.fireAfterProperties(...)` currently only includes `CLIENT_NAME_KEY` and `SECRET_KEY`, so the cookie is always null to connection listeners.

### Fix Focus Areas
- Generate the cookie once and reuse the same value:
  - include `JnlpConnectionState.COOKIE_KEY` in the `fireAfterProperties` property map (or equivalent state property setter), and
  - continue to set the same cookie in the response header.
- (Optional but consistent) if reconnect semantics require it, accept an incoming cookie header on subsequent connect attempts and set it into the state properties.

### Fix Focus Areas (code refs)
- core/src/main/java/jenkins/agents/WebSocketAgents.java[87-102]
- core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java[122-160]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +139 to +143
try {
connection = new ServerSideImpl(new OutputImpl(), authentication);
} catch (IOException x) {
error(x);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Cli ws close npe 🐞 Bug ☼ Reliability

CLIAction.doWs returns from opened() on IOException without closing the WebSocket, leaving
connection null; later closed() unconditionally calls connection.handleClose(), which can throw
NullPointerException and obscure the original failure.
Agent Prompt
### Issue description
In `CLIAction.doWs`, `opened()` catches an `IOException`, logs via `error(x)`, and returns without initializing `connection` or closing the session. Subsequent `binary(...)`/`closed(...)` callbacks dereference `connection`.

### Issue Context
`connection` is a field inside the anonymous `WebSocketSession` and is only assigned in `opened()`.

### Fix Focus Areas
- If `ServerSideImpl` initialization fails, close the WebSocket session immediately.
- Make `binary(...)` and `closed(...)` null-safe (e.g., check `connection != null` before use).

### Fix Focus Areas (code refs)
- core/src/main/java/hudson/cli/CLIAction.java[117-174]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +236 to +239
@DataBoundSetter
public void setUserName(String userName) {
this.userName = userName;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Proxy normalization lost 🐞 Bug ≡ Correctness

ProxyConfiguration moved userName/noProxyHost to @DataBoundSetter but setters no longer apply
Util.fixEmptyAndTrim, so blank values remain non-null and can trigger proxy authentication attempts
or change no-proxy matching behavior compared to the constructor path.
Agent Prompt
### Issue description
`ProxyConfiguration` constructor normalizes inputs with `Util.fixEmptyAndTrim`, but the new `@DataBoundSetter` methods store raw strings. With databinding (UI/config XML), empty strings are common and will now remain non-null.

### Issue Context
`newAuthenticator()` enables proxy auth whenever `userName != null`, so an empty-string username can now cause proxy auth attempts that previously would not have happened.

### Fix Focus Areas
- Apply `Util.fixEmptyAndTrim` (or equivalent) in setters:
  - `setUserName`, `setNoProxyHost`, and likely `setTestUrl`.
- Consider reinitializing `authenticator` when username/password changes if required.

### Fix Focus Areas (code refs)
- core/src/main/java/hudson/ProxyConfiguration.java[137-159]
- core/src/main/java/hudson/ProxyConfiguration.java[226-244]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.