Skip to content
This repository has been archived by the owner on Apr 4, 2022. It is now read-only.

Proxy with Authentication #87

Open
crockpotveggies opened this issue Jan 20, 2016 · 33 comments
Open

Proxy with Authentication #87

crockpotveggies opened this issue Jan 20, 2016 · 33 comments
Projects

Comments

@crockpotveggies
Copy link

It appears that scalaj may not support Proxies with authentication. After attempting to set a Proxy-Authorization header and passing an appropriate proxy config to the request object, I get the following error:

ava.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 proxy authorization required"
  at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:2085)
  at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
  at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
  at scalaj.http.BaseHttp$$anonfun$apply$8.apply(Http.scala:638)
  at scalaj.http.BaseHttp$$anonfun$apply$8.apply(Http.scala:638)
  at scalaj.http.HttpRequest.exec(Http.scala:293)
  at scalaj.http.HttpRequest.execute(Http.scala:268)
  at scalaj.http.HttpRequest.asString(Http.scala:483)
@crockpotveggies
Copy link
Author

After coordinating with a couple people on this one, it appears that there's an underlying issue that I'm hoping may be addressable within the current version of scalaj.

Because the endpoint beyond the proxy is HTTPS, my Proxy-Authorization header is being stripped on the initial CONNECT with the proxy server.

Is there a way to modify the connection to ensure that the header stays put?

@hoffrocket
Copy link
Member

hi, i'm not sure, could you send a code example for the request your making? also, a public authenticated proxy or explanation of how to create one would be helpful for me to reproduce

@crockpotveggies
Copy link
Author

You can set up a free trial at http://proxymesh.com/ which can give you some credentials and a proxy to connect. Steps to reproduce the problem (and code) are as follows:

  • Make an HTTPS connection to a destination beyond the proxy server.
  • Add a proxy host and port to scalaj
  • Add a Proxy-Authorization header with base64-encoded credentials in the form of user:pass
  • Initiate the connection, which will receive a 407 response code
val request = 
  Http("https://github.com/")
    .method("GET")
    .proxy("somehost.proxy.com", 32017)
    .header("Proxy-Authorization","Basic 1b2a3s4e56647e8n9c0o1d2e3c4r5e6d7s")

println(request.asString.body)

@hoffrocket
Copy link
Member

I think you might need to set a global Authenticator to handle this. See this answer: http://stackoverflow.com/a/16340273

It's a bit gross to have to set a global like this, but let me know if this works. Maybe it can be baked into this library:

import java.net.{Authenticator, PasswordAuthentication}
def setProxyCreds(proxyHost: String, proxyPort: Int, proxyUser: String, proxyPassword: String): Unit = {
  Authenticator.setDefault(new Authenticator() {
    override def getPasswordAuthentication(): PasswordAuthentication = {
      if (getRequestorType() == Authenticator.RequestorType.PROXY) {
        if (getRequestingHost().equalsIgnoreCase(proxyHost) && getRequestingPort() == proxyPort) {
          new PasswordAuthentication(proxyUser, proxyPassword.toCharArray())
        }
      }
      null
    }
  })
}

@crockpotveggies
Copy link
Author

I tried this very same solution and wasn't able to get it to work, unfortunately. I also tried setting the global System.setProperty and experienced the same failure.

Is it possible that scalaj doesn't use the default authenticator when initiating the connect? Were you able to successfully apply your solution?

@hoffrocket
Copy link
Member

I haven't tested myself, but scalaj uses the HTTPURLConnection under the
covers, so I was thinking that would work. If you add some printlns to the
Authenticator, is it at least being called?

On Wed, Jan 20, 2016 at 3:06 PM, crockpotveggies notifications@github.com
wrote:

I tried this very same solution and wasn't able to get it to work,
unfortunately. I also tried setting the global System.setProperty and
experienced the same failure.

Is it possible that scalaj doesn't use the default authenticator when
initiating the connect? Were you able to successfully apply your solution?


Reply to this email directly or view it on GitHub
#87 (comment).

@crockpotveggies
Copy link
Author

Ran a few tests and can confirm that the Authenticator isn't being called. Does scalaj use its own classloader or anything else unrelated?

@hoffrocket
Copy link
Member

No, it's not really doing any crazy tricks. The source is pretty
straightforward:
https://github.com/scalaj/scalaj-http/blob/master/src/main/scala/scalaj/http/Http.scala
Unfortunately, I won't have a chance to look into this in more detail until
next week. Please let me know if you discover anything.

On Wed, Jan 20, 2016 at 4:58 PM, crockpotveggies notifications@github.com
wrote:

Ran a few tests and can confirm that the Authenticator isn't being called.
Does scalaj use its own classloader or anything else unrelated?


Reply to this email directly or view it on GitHub
#87 (comment).

@crockpotveggies
Copy link
Author

The nice folks at WonderProxy took a stab at the problem, and apparently had some results. The difference between their solution and my solution, is that they changed the default authenticator after instantiating an Http object.

import scalaj.http._
import java.net.{Authenticator,PasswordAuthentication}

val request = Http("https://github.com/").proxy("buffalo.wonderproxy.com", 10000)
Authenticator.setDefault(new Authenticator() {
  override def getPasswordAuthentication(): PasswordAuthentication = {
    new PasswordAuthentication( "user", "password".toCharArray())
  }
})
println(request.asString.body)

My solution was doing the opposite. Any ideas why this seems to matter?

@hoffrocket
Copy link
Member

No, I wouldn't think it would matter. Building the HttpRequest doesn't
actually do anything, it just stores some state in a POJO. If you reverse
things now do you still see the problem? Maybe something else was going on?

On Thu, Jan 21, 2016 at 11:15 AM, crockpotveggies notifications@github.com
wrote:

The nice folks at WonderProxy took a stab at the problem, and apparently
had some results. The difference between their solution and my solution, is
that they changed the default authenticator after instantiating an Http
object.

import scalaj.http._
import java.net.{Authenticator,PasswordAuthentication}

val request = Http("https://github.com/").proxy("buffalo.wonderproxy.com", 10000)
Authenticator.setDefault(new Authenticator() {
override def getPasswordAuthentication(): PasswordAuthentication = {
new PasswordAuthentication( "user", "password".toCharArray())
}
})
println(request.asString.body)

My solution was doing the opposite. Any ideas why this seems to matter?


Reply to this email directly or view it on GitHub
#87 (comment).

@crockpotveggies
Copy link
Author

Going to test this later today when I have the time, but I'm starting to wonder if this could have been at all affected by dependency injection. I'm going to have to think about our application architecture through the whole stack.

@hoffrocket
Copy link
Member

Closing. plz re-open if there is still an issue.

@raxkin
Copy link

raxkin commented Mar 2, 2017

I still have this problem. I am using the code that suggested @crockpotveggies but i still recive this error:
Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required"

This is the code i used:

import scalaj.http._
import java.net.{Authenticator,PasswordAuthentication}

val request = Http("https://github.com/").proxy("buffalo.wonderproxy.com", 10000)
Authenticator.setDefault(new Authenticator() {
  override def getPasswordAuthentication(): PasswordAuthentication = {
    new PasswordAuthentication( "user", "password".toCharArray())
  }
})
println(request.asString.body)

@hoffrocket
Copy link
Member

Does the equivalent curl command work for you? I don't have access to an authenticated proxy to test myself
curl -v -U user:password -x buffalo.wonderproxy.com:10000 --url http://httpbin.org/

@hoffrocket
Copy link
Member

Also, going back the originally reported problem, I don't see the Proxy-Authorization header being stripped off the request on my systems OSX Java 1.8.0_92 and on Linux Java 1.7.0_79

I even setup a lightweight proxy server using nginx to test out. And then executed this request to verify all the headers being passed along:

scala> val request = Http("http://httpbin.org/get").proxy("localhost", 8888).header("Proxy-Authorization", "Basic dGVzdDp0ZXN0").auth("test","test").asString
request: scalaj.http.HttpResponse[String] = 
HttpResponse({
  "args": {}, 
  "headers": {
    "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2", 
    "Accept-Encoding": "gzip,deflate", 
    "Authorization": "Basic dGVzdDp0ZXN0", 
    "Host": "httpbin.org", 
    "Proxy-Authorization": "Basic dGVzdDp0ZXN0", 
    "Proxy-Connection": "keep-alive", 
    "User-Agent": "scalaj-http/1.0"
  }, 
  "origin": "127.0.0.1, 67.151.197.146", 
  "url": "http://httpbin.org/get"
}
...

nginx.conf:

worker_processes  1;
daemon off;
error_log /dev/stdout info;
events {
    worker_connections  1024;
}

http {
  access_log /dev/stdout;

  server {
    listen 8888;
    server_name localhost;


    location / {
      resolver 8.8.8.8;
      proxy_pass http://$http_host$uri$is_args$args;
      proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
      proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
      proxy_set_header  X-Forwarded-Proto $scheme;
      proxy_read_timeout                  900;
    }
  }
}

@raxkin
Copy link

raxkin commented Mar 3, 2017

Yes, the curl command working for me.
I also tried sending the Proxy-Authorization header but it don't work for me.

@hoffrocket
Copy link
Member

What OS and Java version are you using?

@raxkin
Copy link

raxkin commented Mar 6, 2017

OS: Linux dell 4.9.0-1-amd64 #1 SMP Debian 4.9.2-2 (2017-01-12) x86_64 GNU/Linux
JAVA: 1.8.0_121

@hoffrocket
Copy link
Member

Hi, can you try cloning the source and running ./sbt test ? I added some unit tests to verify that the Proxy-Authorization was being passed through correctly.

@hoffrocket hoffrocket reopened this Mar 6, 2017
@raxkin
Copy link

raxkin commented Mar 6, 2017

Failed 2 tests. This is the output of the 2 fails:

[error] Test scalaj.http.HttpTest.proxyNoAuthTest failed: java.net.SocketTimeoutException: Read timed out, took 5.043 sec
[error]     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[error]     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
[error]     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
[error]     at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
[error]     at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1926)
[error]     at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1921)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1920)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1490)
[error]     at sun.net.www.protocol.http.HttpURLConnection.access$200(HttpURLConnection.java:91)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1466)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1464)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at java.security.AccessController.doPrivilegedWithCombiner(AccessController.java:782)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1463)
[error]     at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
[error]     at scalaj.http.HttpRequest.scalaj$http$HttpRequest$$doConnection(Http.scala:367)
[error]     at scalaj.http.HttpRequest.exec(Http.scala:343)
[error]     at scalaj.http.HttpRequest.asString(Http.scala:490)
[error]     at scalaj.http.HttpTest$$anonfun$proxyNoAuthTest$1$$anonfun$apply$4.apply(HttpTest.scala:331)
[error]     at scalaj.http.HttpTest$$anonfun$proxyNoAuthTest$1$$anonfun$apply$4.apply(HttpTest.scala:330)
[error]     at scalaj.http.HttpTest.makeRequest(HttpTest.scala:43)
[error]     at scalaj.http.HttpTest$$anonfun$proxyNoAuthTest$1.apply(HttpTest.scala:330)
[error]     at scalaj.http.HttpTest$$anonfun$proxyNoAuthTest$1.apply(HttpTest.scala:326)
[error]     at scalaj.http.HttpTest.makeProxiedRequest(HttpTest.scala:104)
[error]     at scalaj.http.HttpTest.proxyNoAuthTest(HttpTest.scala:326)
[error]     ...
[error] Caused by: java.net.SocketTimeoutException: Read timed out
[error]     at java.net.SocketInputStream.socketRead0(Native Method)
[error]     at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
[error]     at java.net.SocketInputStream.read(SocketInputStream.java:171)
[error]     at java.net.SocketInputStream.read(SocketInputStream.java:141)
[error]     at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
[error]     at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
[error]     at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
[error]     at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
[error]     at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
[error]     at sun.net.www.protocol.http.HttpURLConnection.access$200(HttpURLConnection.java:91)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1466)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1464)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at java.security.AccessController.doPrivilegedWithCombiner(AccessController.java:782)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1463)
[error]     at scalaj.http.HttpRequest.scalaj$http$HttpRequest$$doConnection(Http.scala:365)
[error]     ... 64 more
[error] Test scalaj.http.HttpTest.proxyCorrectAuthTest failed: java.net.SocketTimeoutException: Read timed out, took 5.025 sec
[error]     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[error]     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
[error]     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
[error]     at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
[error]     at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1926)
[error]     at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1921)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1920)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1490)
[error]     at sun.net.www.protocol.http.HttpURLConnection.access$200(HttpURLConnection.java:91)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1466)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1464)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at java.security.AccessController.doPrivilegedWithCombiner(AccessController.java:782)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1463)
[error]     at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
[error]     at scalaj.http.HttpRequest.scalaj$http$HttpRequest$$doConnection(Http.scala:367)
[error]     at scalaj.http.HttpRequest.exec(Http.scala:343)
[error]     at scalaj.http.HttpRequest.asString(Http.scala:490)
[error]     at scalaj.http.HttpTest$$anonfun$proxyCorrectAuthTest$1$$anonfun$apply$8.apply(HttpTest.scala:358)
[error]     at scalaj.http.HttpTest$$anonfun$proxyCorrectAuthTest$1$$anonfun$apply$8.apply(HttpTest.scala:357)
[error]     at scalaj.http.HttpTest.makeRequest(HttpTest.scala:43)
[error]     at scalaj.http.HttpTest$$anonfun$proxyCorrectAuthTest$1.apply(HttpTest.scala:357)
[error]     at scalaj.http.HttpTest$$anonfun$proxyCorrectAuthTest$1.apply(HttpTest.scala:353)
[error]     at scalaj.http.HttpTest.makeProxiedRequest(HttpTest.scala:104)
[error]     at scalaj.http.HttpTest.proxyCorrectAuthTest(HttpTest.scala:353)
[error]     ...
[error] Caused by: java.net.SocketTimeoutException: Read timed out
[error]     at java.net.SocketInputStream.socketRead0(Native Method)
[error]     at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
[error]     at java.net.SocketInputStream.read(SocketInputStream.java:171)
[error]     at java.net.SocketInputStream.read(SocketInputStream.java:141)
[error]     at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
[error]     at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
[error]     at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
[error]     at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
[error]     at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
[error]     at sun.net.www.protocol.http.HttpURLConnection.access$200(HttpURLConnection.java:91)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1466)
[error]     at sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1464)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at java.security.AccessController.doPrivilegedWithCombiner(AccessController.java:782)
[error]     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1463)
[error]     at scalaj.http.HttpRequest.scalaj$http$HttpRequest$$doConnection(Http.scala:365)
[error]     ... 64 more
[error] Failed: Total 51, Failed 2, Errors 0, Passed 49
[error] Failed tests:
[error] 	scalaj.http.HttpTest
[error] (test:test) sbt.TestsFailedException: Tests unsuccessful

@hoffrocket
Copy link
Member

Sorry, a recent test code change I made was causing this failure on certain systems. I reverted that change. Could you pull the latest and try again?

@raxkin
Copy link

raxkin commented Mar 7, 2017

All the tests success now.

@hoffrocket
Copy link
Member

Great, then the proxying should work from your system. Can you try this from the console:

./sbt console
val request = Http("http://httpbin.org/get").proxy("proxyhost", proxyport).proxyAuth("username", "password").asString

I added a helper method to set the proxy-authorization header.

@raxkin
Copy link

raxkin commented Mar 8, 2017

Ok, thanks, i will test it as soon as i can today and give you a feedback.

@raxkin
Copy link

raxkin commented Mar 8, 2017

I made some tests and this are the results:
It give me the error HTTP/1.1 407 Proxy Authentication Required when i try to access to https urls.
This returns me the correct result ip:
val request = scalaj.http.Http("http://api.ipify.org").proxy("proxyhost, proxyport).proxyAuth("username", "password").asString

This give me the Authentication error:
val request = scalaj.http.Http("https://api.ipify.org").proxy("proxyhost, proxyport).proxyAuth("username", "password").asString

Then i assume that https is not supported? I can navigate to this url from the browser and with curl without problems with http and https.

@hoffrocket hoffrocket added this to In Progress in scalajhttp Mar 10, 2017
@karimagnusson
Copy link

Hi,
In my sbt file I have:
libraryDependencies += "org.scalaj" %% "scalaj-http" % "2.3.0"
But if I try to compile:
val request = Http("http://httpbin.org/get").proxy("proxyhost", 80).proxyAuth("username", "password").asString
I get the following error:
value proxyAuth is not a member of scalaj.http.HttpRequest

@clement-bramy
Copy link

yes, same thing, never made it to released version?

@abhinavgazta
Copy link

The proxy thing doesn't work? Does someone have a solution.

@abhinavgazta
Copy link

In build.sbt -:
"org.scalaj" %% "scalaj-http" % "2.3.0",

Tried this -:
val resp = Http(s"$url").proxy(proxy.host, proxy.port, Proxy.Type.HTTP)
.auth(proxy.userName.get, proxy.secret.get)
.method("GET").asBytes
Always get this, despite trying eveything mentioned in all the above threads. Does someone know what is the solution for this or do we dump scalaj and pick up akka. ?

Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Unauthorized"
java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Unauthorized"

@hoffrocket
Copy link
Member

Hi @abhinavgazta -- can you try upgrading to version "2.4.1" and pass your proxy credentials using a new proxyAuth method instead of auth

@abhinavgazta
Copy link

Hey, i did try with the new proxyAuth method, still same 407 issue. Doesn't work with it
Http(goodUrl).proxyAuth("user", "pwd").proxy(
host, port, Proxy.Type.HTTP).method("GET").asString

Http(goodUrl).proxyAuth("user", "pwd").proxy(
host, port).method("GET").asString

Works with akka http using same proxy -:
With akka -:
import akka.http.scaladsl.model.{headers, _}
val response: Future[HttpResponse] = Http().singleRequest(HttpRequest().withMethod(HttpMethods.GET).withUri(url), settings = settings)

Not sure why its still not working with scalaj.http

@abhinavgazta
Copy link

Also i think it might not be working for https proxies. HTTP might work.

@hoffrocket
Copy link
Member

Yes, HTTPS proxies with authentication are not supported correctly due to the underlying reliance on the JDK's built-in http libraries. See here for detailed discussion on the fundamental problem and possible solution: https://stackoverflow.com/questions/34877470/basic-proxy-authentication-for-https-urls-returns-http-1-0-407-proxy-authenticat/34980074

I don't plan on fixing that at this time, but I would welcome a well tested PR

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
scalajhttp
In Progress
Development

No branches or pull requests

6 participants