This is an automated email from the ASF dual-hosted git repository.

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 07e99d65bac46e7307160a1837eddef26e897265
Author: Paul King <[email protected]>
AuthorDate: Mon May 11 16:25:04 2026 +1000

    user agent workaround
---
 .../src/main/groovy/org.apache.groovy-tested.gradle | 13 ++++++++++++-
 .../main/groovy/groovy/grape/ivy/GrapeIvy.groovy    | 21 +++++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
index 8758584af6..1546a8f84e 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
@@ -49,7 +49,18 @@ def aggregator = TestResultAggregatorService.register(
 tasks.withType(Test).configureEach {
     def fs = objects.newInstance(TestServices).fileSystemOperations
     def grapeDirectory = new File(temporaryDir, '.groovy')
-    def options = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}", 
'-Duser.language=en', '-Duser.country=US']
+    // GROOVY-12005 / 2026-05: Maven Central (Fastly) returns HTTP 404 with a 
1-byte
+    // body for requests bearing the JDK URLConnection default User-Agent 
("Java/<version>"),
+    // which Ivy's BasicURLHandler relies on. We must set http.agent as a JVM 
argument
+    // here rather than via systemProperty / a static initializer in GrapeIvy: 
by the
+    // time GrapeIvy's class init runs (lazily on first @Grab), the test JVM 
has already
+    // loaded HttpURLConnection and its agent is locked in. Passing it as 
`-Dhttp.agent`
+    // on the JVM command line guarantees it's set before any class is 
initialized.
+    // Tests pass with literally any non-"Java/*" value; see also the matching 
static
+    // initializer in GrapeIvy for CLI / standalone-script coverage.
+    def options = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}",
+                   '-Duser.language=en', '-Duser.country=US',
+                   '-Dhttp.agent=Apache-Ivy_Groovy-Grape']
     jvmArgs(*options)
     systemProperty 'groovy.force.illegal.access', 
findProperty('groovy.force.illegal.access')
     def testdb = System.properties['groovy.testdb.props']
diff --git 
a/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy 
b/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy
index 06f7c9218a..52828b5a2c 100644
--- 
a/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy
+++ 
b/subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy
@@ -66,6 +66,27 @@ import java.util.regex.Pattern
 @AutoFinal @CompileStatic
 class GrapeIvy implements GrapeEngine {
 
+    static {
+        // GROOVY-12005 / 2026-05: Maven Central (Fastly) currently returns 
HTTP 404
+        // with a 1-byte body for requests whose User-Agent is the JDK 
URLConnection
+        // default "Java/<version>". Any other value (curl/x.y, 
Apache-Ivy/..., etc.)
+        // gets the expected HTTP 200. Ivy 2.5.3 uses java.net.URLConnection 
under
+        // the hood, which honours -Dhttp.agent. We set it here so direct 
grape use
+        // (CLI `grape install`, `groovy [email protected]`, embedded 
usage)
+        // gets a working agent — those paths load GrapeIvy before any other 
class
+        // touches the network, so this static init runs early enough.
+        //
+        // For test JVMs the same property MUST be set on the JVM command line 
—
+        // see the matching -Dhttp.agent in 
build-logic/.../org.apache.groovy-tested.gradle —
+        // because by the time GrapeIvy is class-loaded for the first @Grab in 
a test,
+        // the agent has already been cached deep in the JDK's HTTP machinery.
+        //
+        // Respect any value the user has already configured.
+        if (System.getProperty('http.agent') == null) {
+            System.setProperty('http.agent', 'Apache-Ivy_Groovy-Grape')
+        }
+    }
+
     private static final List<String> DEFAULT_CONF = 
Collections.singletonList('default')
     private static final Map<String, Set<String>> MUTUALLY_EXCLUSIVE_KEYS = 
processGrabArgs([
             ['group', 'groupId', 'organisation', 'organization', 'org'],

Reply via email to