Unable to tunnel through proxy

I am trying (for the first time) to build a local fork of JMonkeyEngine with gradle 8.9, but the build failed because of being unable to tunnel through my network proxy.

* Where:
Build file '/home/codex/java/prj/jmonkeyengine/build.gradle' line: 219

* What went wrong:
Execution failed for task ':getNativesZipFile'.
> java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required"

I’ve added the appropriate settings to /home/[username]/.gradle/gradle.properties and confirmed that they are correct by building another project requiring a network connection.

systemProp.http.proxyHost=***.***.*.**
systemProp.http.proxyPort=****
systemProp.http.proxyUser=*********
systemProp.http.proxyPassword=*********
systemProp.https.proxyHost=***.***.*.**
systemProp.https.proxyPort=****
systemProp.https.proxyUser=*********
systemProp.https.proxyPassword=*********

I’d tend to say this is an Ant bug you should report to Ant.
That build is using the Ant get task to download a file: jmonkeyengine/build.gradle at 1e10225f746b5c1ffe3eaca6d9f69c1421ea403f · jMonkeyEngine/jmonkeyengine · GitHub
Ant seems to consider proxyHost and proxyPort, but not username and password.

If one uses the setproxy Ant task before the get task is executed to set the proxy information, it works properly.

So you could also consider additionally reporting an improvement request to Gradle, so that Gradle is automatically doing the setproxy task when necessary.

As a work-around you could either modify the build script of that build locally to do the setproxy task first, or write an init script that does it.
For the latter, create <GRADLE_USER_HOME>/init.d/set-proxy-for-ant.gradle with content

rootProject {
    ant.setproxy(proxyhost: 'localhost', proxyport: '8888', proxyuser: 'a', proxypassword: 'b')
}

with your values, or read the values from the system properties, or whatever you like.

1 Like

I added /home/[username]/.gradle/init.d/set-proxy-for-ant.gradle:

rootProject {
    println("configure proxy settings for ant");
    ant.setproxy(proxyhost: '...', proxyport: '...', proxyuser: '...', proxypassword: '...');
}

The println confirms the script is running, but ant is still unable to get through the proxy. I also tried modifying gradle.build in the repository, which didn’t work either.

Ah, you should rename the file to set-proxy-for-ant.init.gradle.
Shouldn’t make a difference here, but is more clean and works better if you edit the file in some IDE.

As the task is in the root project of that build, the init script should work.
But maybe to be on the safe you’d want to do it in allprojects { ... } instead of rootproject { ... }.

I don’t have a proxy with auth available, but I used Fiddler to fake it and with that init script it caused a second request to be made including the proxy auth header as necessary.

Hmm, changed to allprojects but it still doesn’t work (same error). Changing the proxyhost or proxyport results in different errors, so setproxy is at least doing something here, just not the authentication process.

Try running with --debug to get some more information out of it.
When I did that here, I was seeing the 407 responses and so on logged.

You might also try to debug into the SetProxy class to see whether it does the work it is supposed to do properly.

I’m not sure how to do debugging like that. I’m kinda new to all this. :slight_smile:

I did manage piece together how SetProxy works, so I tried setting things up manually.

import java.net.Authenticator;
import java.net.PasswordAuthentication;

class ProxyAuth extends Authenticator {

    private PasswordAuthentication auth;

    private ProxyAuth(String user, String pass) {
        auth = new PasswordAuthentication(user, pass.toCharArray());
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        println("proxy authentication requested");
        return auth;
    }
    
}

String host = '************'
String port = '****'
String username = '*************'
String password = '*************'

allprojects {
    println("configuring proxy settings for ant: "+host+", "+port+", "+username+", "+password);
    var props = System.getProperties();
    props.put('https.proxyHost', host);
    props.put('https.proxyPort', port);
    props.put('https.proxyUser', username);
    props.put('https.proxyPassword', password);
    props.put('http.proxyHost', host);
    props.put('http.proxyPort', port);
    props.put('http.proxyUser', username);
    props.put('http.proxyPassword', password);
    ProxyAuth auth = new ProxyAuth(username, password);
    Authenticator.setDefault(auth);
    if (Authenticator.getDefault() == auth) {
        println("successfully registered authenticator");
    }
}

The same error still occurs, but I’m reasonably sure this should work because changing host results in the network being unreachable altogether (expected). What is interesting is that getPasswordAuthentication never seems to be called, which would explain why ant cannot get through the proxy.

Edit: I did also try building with --debug. Here is the debug output for getNativesZipFile (some info is redacted):

2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Task :getNativesZipFile' started
2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Snapshot inputs and outputs before executing task ':getNativesZipFile'' started
2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Snapshot inputs and outputs before executing task ':getNativesZipFile''
2024-08-05T11:38:49.791-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Executing task ':getNativesZipFile'' started
2024-08-05T11:38:49.791-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Execute doFirst {} action for :getNativesZipFile' started
2024-08-05T11:38:49.811-0400 [DEBUG] [sun.net.www.protocol.http.HttpURLConnection] sun.net.www.MessageHeader@7efae9b35 pairs: {CONNECT objects.jmonkeyengine.org:443 HTTP/1.1: null}{User-Agent: Java/11.0.21}{Host: objects.jmonkeyengine.org}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Proxy-Connection: keep-alive}
2024-08-05T11:38:49.840-0400 [DEBUG] [sun.net.www.protocol.http.HttpURLConnection] sun.net.www.MessageHeader@703d081c14 pairs: {null: HTTP/1.1 407 Proxy Authentication Required}{Server: ********}{Mime-Version: 1.0}{Date: Mon, 05 Aug 2024 15:38:49 GMT}{Content-Type: text/html;charset=utf-8}{Content-Length: 3657}{*******-Error: ERR_CACHE_ACCESS_DENIED 0}{Vary: Accept-Language}{Content-Language: en}{Proxy-Authenticate: Basic realm="******** proxy web server"}{X-Cache: MISS from ********}{X-Cache-Lookup: NONE from *********:******}{Via: 1.1 ********* (********)}{Connection: keep-alive}
2024-08-05T11:38:49.840-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Execute doFirst {} action for :getNativesZipFile'
2024-08-05T11:38:49.840-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Executing task ':getNativesZipFile''
2024-08-05T11:38:49.841-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Snapshot outputs after executing task ':getNativesZipFile'' started
2024-08-05T11:38:49.841-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Snapshot outputs after executing task ':getNativesZipFile''
2024-08-05T11:38:49.790-0400 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger] 
2024-08-05T11:38:49.790-0400 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger] > Task :getNativesZipFile FAILED
2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter] Putting task artifact state for task ':getNativesZipFile' into context took 0.0 secs.
2024-08-05T11:38:49.790-0400 [INFO] [org.gradle.internal.watch.registry.impl.NonHierarchicalFileWatcherUpdater] Watching 1 directories to track changes
2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep] Implementation for task ':getNativesZipFile': org.gradle.api.DefaultTask_Decorated@db41185a6cd1219ee7052202f5c9a914
2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep] Additional implementations for task ':getNativesZipFile': [_BuildScript_$_run_closure14$_closure40_****************************]
2024-08-05T11:38:49.790-0400 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Build operation 'Snapshot inputs and outputs before executing task ':getNativesZipFile'' completed
2024-08-05T11:38:49.791-0400 [INFO] [org.gradle.internal.execution.steps.ResolveCachingStateStep] Caching disabled for task ':getNativesZipFile' because:
  Build cache is disabled
2024-08-05T11:38:49.791-0400 [DEBUG] [org.gradle.internal.execution.steps.SkipUpToDateStep] Determining if task ':getNativesZipFile' is up-to-date
2024-08-05T11:38:49.791-0400 [INFO] [org.gradle.internal.execution.steps.SkipUpToDateStep] Task ':getNativesZipFile' is not up-to-date because:
  Task has failed previously.

Ah, so probably a third bug you should report.
I did try it with an HTTP URL and there it works.
With the HTTPS URL it does not work.

Luckily for that build the URL is coming from a Gradle project property, so you can additionally set

PREBUILD_NATIVES_URL=http://objects.jmonkeyengine.org/native-snapshots/${natives.snapshot}/jme3-natives.zip

and it suddenly works, as the HTTP URL triggers the proxy auth to be initiated, then the HTTP URL redirect to the HTTPS URL and at that point the proxy auth is already set up and sent along, so the download works. :roll_eyes:

1 Like

Yes, that fixed the issue of being unable to tunnel through the proxy. Thanks so much for your help! :hugs:

1 Like