Skip to content

Commit 799693c

Browse files
committed
test additional user agent workarounds
1 parent 09e9122 commit 799693c

3 files changed

Lines changed: 41 additions & 24 deletions

File tree

build-logic/src/main/groovy/org.apache.groovy-tested.gradle

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,33 @@ def grapeBridgeCache = (findProperty('groovy.grape.bridge-cache') ?:
6262
tasks.withType(Test).configureEach {
6363
def fs = objects.newInstance(TestServices).fileSystemOperations
6464
def grapeDirectory = new File(temporaryDir, '.groovy')
65-
// GROOVY-12005 / 2026-05: Maven Central (Fastly) returns HTTP 404 with a 1-byte
66-
// body for requests bearing the JDK URLConnection default User-Agent ("Java/<version>"),
67-
// which Ivy's BasicURLHandler relies on. We must set http.agent as a JVM argument
68-
// here rather than via systemProperty / a static initializer in GrapeIvy: by the
69-
// time GrapeIvy's class init runs (lazily on first @Grab), the test JVM has already
70-
// loaded HttpURLConnection and its agent is locked in. Passing it as `-Dhttp.agent`
71-
// on the JVM command line guarantees it's set before any class is initialized.
72-
// Tests pass with literally any non-"Java/*" value; see also the matching static
73-
// initializer in GrapeIvy for CLI / standalone-script coverage.
7465
def options = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}",
75-
'-Duser.language=en', '-Duser.country=US',
76-
'-Dhttp.agent=Apache-Ivy_Groovy-Grape']
66+
'-Duser.language=en', '-Duser.country=US']
7767
jvmArgs(*options)
68+
// GROOVY-12005 / 2026-05: Maven Central (Fastly) returns HTTP 404 with a
69+
// 1-byte body for requests bearing the JDK URLConnection default User-Agent
70+
// ("Java/<version>"), which Ivy 2.5.3's BasicURLHandler relies on. We adopt
71+
// a Maven-shaped UA — Maven Resolver's HTTP transport sends this and the CDN
72+
// doesn't 404 it. The Maven version string is decorative; the CDN's filter
73+
// is on the bare "Java/<version>" default, not on Maven's specific version.
74+
//
75+
// http.agent must reach the test JVM as `-Dhttp.agent=...` on the command
76+
// line: by the time GrapeIvy's matching static initializer runs (lazily on
77+
// first @Grab), HttpURLConnection's userAgent static is already populated
78+
// and our value would arrive too late. Using `systemProperty` rather than
79+
// raw `jvmArgs` preserves the space-containing value as a single argument.
80+
systemProperty 'http.agent',
81+
"Apache-Maven/3.9.14 (Java ${System.getProperty('java.version')}; ${System.getProperty('os.name')} ${System.getProperty('os.version')})"
7882
systemProperty 'groovy.force.illegal.access', findProperty('groovy.force.illegal.access')
7983
def testdb = System.properties['groovy.testdb.props']
8084
if (testdb) {
8185
systemProperty 'groovy.testdb.props', testdb
8286
}
87+
// Forward StrictLocalM2Resolver opt-in to test JVMs. Accepts -P or -D.
88+
def strictLocalM2 = findProperty('groovy.grape.strict-localm2') ?: System.properties['groovy.grape.strict-localm2']
89+
if (strictLocalM2) {
90+
systemProperty 'groovy.grape.strict-localm2', strictLocalM2
91+
}
8392
def headless = System.properties['java.awt.headless']
8493
if (headless == 'true') {
8594
systemProperty 'java.awt.headless', 'true'
@@ -105,7 +114,7 @@ tasks.withType(Test).configureEach {
105114
println "Using ${executable} to run tests"
106115
}
107116

108-
forkEvery = 50
117+
forkEvery = 40
109118
maxParallelForks = sharedConfiguration.isRunningOnCI ? 1 : (Runtime.runtime.availableProcessors().intdiv(2) ?: 1)
110119
scanForTestClasses = true
111120
ignoreFailures = false

subprojects/groovy-grape-ivy/src/main/groovy/groovy/grape/ivy/GrapeIvy.groovy

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,28 @@ class GrapeIvy implements GrapeEngine {
6969
static {
7070
// GROOVY-12005 / 2026-05: Maven Central (Fastly) currently returns HTTP 404
7171
// with a 1-byte body for requests whose User-Agent is the JDK URLConnection
72-
// default "Java/<version>". Any other value (curl/x.y, Apache-Ivy/..., etc.)
73-
// gets the expected HTTP 200. Ivy 2.5.3 uses java.net.URLConnection under
74-
// the hood, which honours -Dhttp.agent. We set it here so direct grape use
75-
// (CLI `grape install`, `groovy script-with-@Grab.groovy`, embedded usage)
76-
// gets a working agent — those paths load GrapeIvy before any other class
77-
// touches the network, so this static init runs early enough.
72+
// default "Java/<version>". Maven's own requests are not blocked, so we
73+
// adopt a Maven-shaped User-Agent — that's the value Maven Resolver's HTTP
74+
// transport sends, and it passes the CDN cleanly. Ivy 2.5.3's
75+
// BasicURLHandler uses java.net.URLConnection, which honours -Dhttp.agent.
76+
//
77+
// This static initializer covers direct grape use (CLI `grape install`,
78+
// `groovy script-with-@Grab.groovy`, embedded usage) — those paths load
79+
// GrapeIvy before any other class touches the network, so it runs early
80+
// enough that HttpURLConnection's userAgent static init reads our value.
7881
//
7982
// For test JVMs the same property MUST be set on the JVM command line —
80-
// see the matching -Dhttp.agent in build-logic/.../org.apache.groovy-tested.gradle —
81-
// because by the time GrapeIvy is class-loaded for the first @Grab in a test,
82-
// the agent has already been cached deep in the JDK's HTTP machinery.
83+
// see the matching `systemProperty 'http.agent', ...` in
84+
// build-logic/.../org.apache.groovy-tested.gradle — because by the time
85+
// GrapeIvy is class-loaded for the first @Grab in a test, HttpURLConnection
86+
// has already been initialised and its userAgent is locked in.
8387
//
84-
// Respect any value the user has already configured.
88+
// Respect any value the user has already configured (e.g. -Dhttp.agent=…).
8589
if (System.getProperty('http.agent') == null) {
86-
System.setProperty('http.agent', 'Apache-Ivy_Groovy-Grape')
90+
// Maven version is decorative — the CDN's filter is on the bare
91+
// "Java/<version>" default, not on a specific Maven version string.
92+
String ua = "Apache-Maven/3.9.14 (Java ${System.getProperty('java.version')}; ${System.getProperty('os.name')} ${System.getProperty('os.version')})"
93+
System.setProperty('http.agent', ua)
8794
}
8895
}
8996

subprojects/groovy-grape-ivy/src/main/resources/groovy/grape/ivy/defaultGrapeConfig.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
<ivysettings>
2222
<settings defaultResolver="downloadGrapes"/>
2323
<caches lockStrategy="artifact-lock-nio"/>
24+
<typedef name="strict-localm2" classname="groovy.grape.ivy.StrictLocalM2Resolver"/>
2425
<resolvers>
2526
<chain name="downloadGrapes" returnFirst="true">
2627
<filesystem name="cachedGrapes">
2728
<ivy pattern="${user.home}/.groovy/grapes/[organisation]/[module]/ivy-[revision].xml"/>
2829
<artifact pattern="${user.home}/.groovy/grapes/[organisation]/[module]/[type]s/[artifact]-[revision](-[classifier]).[ext]"/>
2930
</filesystem>
30-
<ibiblio name="localm2" root="${user.home.url}/.m2/repository/" checkmodified="true" changingPattern=".*" changingMatcher="regexp" m2compatible="true"/>
31+
<strict-localm2 name="localm2" root="${user.home.url}/.m2/repository/" checkmodified="true" changingPattern=".*" changingMatcher="regexp" m2compatible="true"/>
3132
<!-- TODO: add 'endorsed groovy extensions' resolver here -->
3233
<ibiblio name="ibiblio" m2compatible="true"/>
3334
</chain>

0 commit comments

Comments
 (0)