I have some Scala code that has managed to successfully negotiate the (NTLM) proxy and access the internet, specifying the username and password as so:
// Based upon http://rolandtapken.de/blog/2012-04/java-process-httpproxyuser-and-httpproxypassword
Authenticator.setDefault(new Authenticator() {
override protected def getPasswordAuthentication: PasswordAuthentication = {
if (getRequestorType eq RequestorType.PROXY) {
val prot = getRequestingProtocol.toLowerCase
// This allows us to only return a PasswordAuthentication when we have
// all four of the host, port, user and password _and_ the host and
// port match the actual proxy wanting authentication.
for {
host <- Option(System.getProperty(prot + ".proxyHost"))
if getRequestingHost.equalsIgnoreCase(host)
port <- Try(augmentString(System.getProperty(prot + ".proxyPort")).toInt).toOption
if port == getRequestingPort
user <- Option(System.getProperty(prot + ".proxyUser"))
pass <- Option(System.getProperty(prot + ".proxyPassword"))
} yield return new PasswordAuthentication(user, pass.toCharArray)
}
// One of the if-statements failed. No authentication for you!
null
}
})
However, I've now been given a new system username/password combination to use, and the password contains an @
in it. I've tried using the password directly, escaping it (with both \
and \\
in case a double-level of escaping was needed), url-encoding it (i.e. replacing @
with %40
) and even HTML-encoding (@
and @
) to no avail.
I know the password works, as it's used on other systems for non-JVM applications to access the internet by setting the http_proxy
variable, but it doesn't work here.
Any ideas?
EDIT
To try and clear some things up, I've tried simplifying my code to this:
Authenticator.setDefault(new Authenticator() {
def urlEncode(str: String): String = {
val res = URLEncoder.encode(str, "UTF-8")
// To confirm it's working
println(s"${str} -> ${res}")
res
}
override protected def getPasswordAuthentication: PasswordAuthentication = {
if (getRequestorType eq RequestorType.PROXY) {
return new PasswordAuthentication(urlEncode("username"), urlEncode("p@ssword").toCharArray);
}
null
}
})
The environment that this is being run in is in a Spark cluster (run with spark-submit
) on a Linux box. The proxy is a corporate NTLM one.
If I use known a username and password combination that doesn't contain @
then this works. If I change it to one containing an @
then it fails.
I've tried making val res = str
inside the urlEncode
function (in case it doesn't need to be URL encoded), tried having \\@
(with and without URL encoding) and ^@
(with and without URL encoding). Every single time I get an exception of Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authorization Required"
.
I know that the username and password are valid as they are currently set in the https_proxy
variable that is successfully used by curl, etc.
So unless the fact that the proxy is being set within a running Spark server somehow affects what happens to it, it appears to me that the JVM libraries do not support having @
in the authenticator for proxies (at the very least).