Skip to content

[java] #16753 workaround for JDK bug JDK-8228970#16793

Merged
asolntsev merged 1 commit intoSeleniumHQ:trunkfrom
asolntsev:fix/16753-read-all-bytes-jdk-bug
Dec 24, 2025
Merged

[java] #16753 workaround for JDK bug JDK-8228970#16793
asolntsev merged 1 commit intoSeleniumHQ:trunkfrom
asolntsev:fix/16753-read-all-bytes-jdk-bug

Conversation

@asolntsev
Copy link
Contributor

@asolntsev asolntsev commented Dec 24, 2025

User description

Replace all usages of JDK built-in method stream.readAllBytes() by our own (fixed) method Read.toByteArray(stream).

🔗 Related Issues

#16753

JDK bug: https://bugs.openjdk.org/browse/JDK-8228970

🔄 Types of changes

  • Bug fix (backwards compatible)

PR Type

Bug fix


Description

  • Create new Read utility class with workaround for JDK-8228970 bug

  • Replace all stream.readAllBytes() calls with Read.toByteArray()

  • Add helper methods toString() and resourceAsString() for convenience

  • Improve resource stream handling with proper try-with-resources patterns


Diagram Walkthrough

flowchart LR
  A["JDK readAllBytes bug<br/>JDK-8228970"] -->|"Replace with"| B["Read.toByteArray<br/>custom implementation"]
  B -->|"Used by"| C["CdpEventTypes"]
  B -->|"Used by"| D["RemoteScript"]
  B -->|"Used by"| E["W3CHttpCommandCodec"]
  B -->|"Used by"| F["UrlChecker"]
  B -->|"Used by"| G["HttpMessage"]
  B -->|"Used by"| H["JdkHttpMessages"]
Loading

File Walkthrough

Relevant files
Enhancement
1 files
Read.java
New utility class for safe stream reading                               
+65/-0   
Bug fix
6 files
CdpEventTypes.java
Replace readAllBytes with Read.toString                                   
+2/-2     
UrlChecker.java
Replace readAllBytes with Read.toByteArray                             
+12/-8   
RemoteScript.java
Use Read.resourceAsString for script loading                         
+2/-11   
W3CHttpCommandCodec.java
Replace readAllBytes with resourceAsString                             
+2/-10   
HttpMessage.java
Replace readAllBytes with Read.toByteArray                             
+2/-1     
JdkHttpMessages.java
Replace readAllBytes with Read.toByteArray                             
+3/-1     
Tests
1 files
ReadTest.java
Add unit tests for Read utility class                                       
+34/-0   
Configuration changes
2 files
BUILD.bazel
Add io module dependency to selenium                                         
+1/-0     
BUILD.bazel
Expand visibility for io module                                                   
+1/-0     

@asolntsev asolntsev linked an issue Dec 24, 2025 that may be closed by this pull request
@selenium-ci selenium-ci added C-java Java Bindings B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related labels Dec 24, 2025
@asolntsev asolntsev added this to the 4.40.0 milestone Dec 24, 2025
@asolntsev asolntsev self-assigned this Dec 24, 2025
@asolntsev asolntsev added I-defect Something is not working as intended I-regression Something was working but we "fixed" it labels Dec 24, 2025
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 24, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #16753
🟢 Prevent getScreenshotAs from throwing AssertionError in
org.openqa.selenium.remote.http.jdk.JdkHttpMessages.readResponseBody when running on Java
11 (Windows), by avoiding the problematic Java 11 InputStream.readAllBytes() behavior.
Provide a workaround compatible with Java 11 while keeping behavior correct on newer JDKs.

Verify the reported reproduction on Windows + JDK 11 (e.g., ChromeDriver +
TakesScreenshot#getScreenshotAs) no longer triggers the AssertionError and does not
introduce regressions on JDK 17+.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing null handling: Read.resourceAsString can pass a null InputStream into Read.toString, causing an unhandled
NullPointerException instead of a contextual exception when a resource is missing.

Referred Code
public static String resourceAsString(String resource) {
  try (InputStream stream = Read.class.getResourceAsStream(resource)) {
    return toString(stream);
  } catch (IOException e) {
    throw new UncheckedIOException("Failed to read resource " + resource, e);
  }

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated resource path: Read.resourceAsString accepts an arbitrary resource path without validation, so a reviewer
should confirm the argument is not user-controlled and cannot be abused to access
unintended classpath resources.

Referred Code
public static String resourceAsString(String resource) {
  try (InputStream stream = Read.class.getResourceAsStream(resource)) {
    return toString(stream);
  } catch (IOException e) {
    throw new UncheckedIOException("Failed to read resource " + resource, e);
  }

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@asolntsev asolntsev removed B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related labels Dec 24, 2025
@asolntsev asolntsev requested a review from cgoldberg December 24, 2025 12:36
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 24, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Handle null stream when resource not found

In resourceAsString, add a null check for the InputStream returned by
getResourceAsStream and throw an IllegalArgumentException if it is null to
prevent a NullPointerException.

java/src/org/openqa/selenium/io/Read.java [58-64]

 public static String resourceAsString(String resource) {
   try (InputStream stream = Read.class.getResourceAsStream(resource)) {
+    if (stream == null) {
+      throw new IllegalArgumentException("Resource not found: " + resource);
+    }
     return toString(stream);
   } catch (IOException e) {
     throw new UncheckedIOException("Failed to read resource " + resource, e);
   }
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential NullPointerException if a resource is not found and proposes a fix that improves robustness and provides a clearer error message.

Medium
Learned
best practice
Close created input streams

Ensure the InputStream returned by supplier.get() is always closed by wrapping
it in a try-with-resources before reading it.

java/src/org/openqa/selenium/remote/http/HttpMessage.java [201-208]

 @Deprecated
 public M setContent(Supplier<InputStream> supplier) {
-  try {
-    return setContent(Contents.bytes(Read.toByteArray(supplier.get())));
+  try (InputStream in = supplier.get()) {
+    return setContent(Contents.bytes(Read.toByteArray(in)));
   } catch (IOException ex) {
     throw new UncheckedIOException(ex);
   }
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Always wrap creation of external resources in try/finally (or try-with-resources) and perform explicit cleanup to prevent leaks even when exceptions occur.

Low
General
Wrap resource errors as state exceptions

In addDomMutationHandler, wrap the call to Read.resourceAsString in a try-catch
block to catch UncheckedIOException and re-throw it as an IllegalStateException
to maintain original exception behavior.

java/src/org/openqa/selenium/remote/RemoteScript.java [89]

-String scriptValue = Read.resourceAsString("/org/openqa/selenium/remote/bidi-mutation-listener.js");
+String scriptValue;
+try {
+  scriptValue = Read.resourceAsString("/org/openqa/selenium/remote/bidi-mutation-listener.js");
+} catch (UncheckedIOException e) {
+  throw new IllegalStateException("Unable to load helper script", e);
+}
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out a change in exception handling behavior and proposes restoring it, which improves consistency with the pre-refactoring code.

Low
  • Update

@asolntsev asolntsev force-pushed the fix/16753-read-all-bytes-jdk-bug branch 2 times, most recently from 8b0de03 to 9808f74 Compare December 24, 2025 12:49
…rg/browse/JDK-8228970

Replace all usages of JDK built-in method `stream.readAllBytes()` by our own (fixed) method `Read.toByteArray(stream)`.
@asolntsev asolntsev force-pushed the fix/16753-read-all-bytes-jdk-bug branch from 9808f74 to 7378ea4 Compare December 24, 2025 12:51
Copy link
Member

@cgoldberg cgoldberg left a comment

Choose a reason for hiding this comment

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

Looks fine.. but is it really necessary to implement an alternative instead of just catching AssertionError that might happen in Java 11 if you try to read an empty InputStream? The existing readAllBytes should work otherwise, right?

@asolntsev
Copy link
Contributor Author

@cgoldberg I don't think it happens only for empty stream.

I manage to reproduce the issue when reading output of "get screenshot" and "get page source" operations which obviously don't return empty stream.

Copy link
Member

@cgoldberg cgoldberg left a comment

Choose a reason for hiding this comment

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

Sounds good. Thanks for fixing this.

@asolntsev asolntsev merged commit 68f1094 into SeleniumHQ:trunk Dec 24, 2025
72 of 73 checks passed
@asolntsev asolntsev deleted the fix/16753-read-all-bytes-jdk-bug branch December 24, 2025 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-java Java Bindings I-defect Something is not working as intended I-regression Something was working but we "fixed" it Review effort 3/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🐛 Bug]: Java 11 - TakeScreenshot causes exception

3 participants