watson-developer-cloud / java-sdk

:1st_place_medal: Java SDK to use the IBM Watson services.
http://watson-developer-cloud.github.io/java-sdk/
Apache License 2.0
594 stars 532 forks source link

Assistant - HTTPS proxy not working? #1152

Closed lwittenb closed 3 years ago

lwittenb commented 3 years ago

Hello,

I have opened a question on stackoverflow, but with no feedback and further investigation, I think there's something wrong. Initial question: https://stackoverflow.com/questions/66174163/ibm-watson-assistant-https-proxy-not-working

Context Java application (jdk 1.8.0_65 and 11.0.6+10), using IBM Watson Assistant API at https://api.eu-de.assistant.watson.cloud.ibm.com, within corporate network through HTTPS proxy.

Issue Previously, I was using:

<dependency>
    <groupId>com.ibm.watson.developer_cloud</groupId>
    <artifactId>java-sdk</artifactId>
    <version>6.13.0</version>
</dependency>

All was fine with the following code (also ok with v6.14.2), which means the session was created and I was getting an ID:

import java.net.InetSocketAddress;
import java.net.Proxy;
import com.ibm.watson.developer_cloud.assistant.v2.Assistant;
import com.ibm.watson.developer_cloud.assistant.v2.model.CreateSessionOptions;
import com.ibm.watson.developer_cloud.assistant.v2.model.SessionResponse;
import com.ibm.watson.developer_cloud.http.HttpConfigOptions;
import com.ibm.watson.developer_cloud.http.ServiceCall;
import com.ibm.watson.developer_cloud.service.security.IamOptions;

[...]

IamOptions iamOptions = new IamOptions.Builder().apiKey(apiKey).build();
Assistant assistant = new Assistant(version, iamOptions);
assistant.setEndPoint(url);

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, (Integer.parseInt(proxyPort))));
HttpConfigOptions httpOptions = new HttpConfigOptions.Builder().proxy(proxy).build();
assistant.configureClient(httpOptions);

CreateSessionOptions options = new CreateSessionOptions.Builder(assistantId).build();
ServiceCall<SessionResponse> serviceCall = assistant.createSession(options);
SessionResponse response = serviceCall.execute();
String sessionId = response.getSessionId();

Now, moving to:

<dependency>
  <groupId>com.ibm.watson</groupId>
  <artifactId>ibm-watson</artifactId>
  <version>8.6.0</version>
</dependency>

On the same environment, the updated code does not work anymore (also with v9.0.2), and keeps raising an exception with the following message only: "Error while fetching access token from token service:"

import java.net.InetSocketAddress;
import java.net.Proxy;
import com.ibm.cloud.sdk.core.http.HttpConfigOptions;
import com.ibm.cloud.sdk.core.http.ServiceCall;
import com.ibm.cloud.sdk.core.security.IamAuthenticator;
import com.ibm.watson.assistant.v2.Assistant;
import com.ibm.watson.assistant.v2.model.CreateSessionOptions;
import com.ibm.watson.assistant.v2.model.SessionResponse;

[...]

IamAuthenticator authenticator = new IamAuthenticator(apiKey);
Assistant assistant = new Assistant(version, authenticator);
assistant.setServiceUrl(url);

HttpConfigOptions httpOptions = new HttpConfigOptions.Builder()
        .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, (Integer.parseInt(proxyPort)))))
        .loggingLevel(HttpConfigOptions.LoggingLevel.BASIC)
        .build();
assistant.configureClient(httpOptions);

CreateSessionOptions options = new CreateSessionOptions.Builder(assistantId).build();
ServiceCall<SessionResponse> serviceCall = assistant.createSession(options);
SessionResponse response = serviceCall.execute().getResult();
String sessionId = response.getSessionId();

I could rollback to java-sdk instead of ibm-watson, but most Constructor and functions are marked as deprecated. If I manually inject the proxy in the system properties, like this:

System.setProperty("https.proxyHost", proxyHost);
System.setProperty("https.proxyPort", proxyPort);

It also works and the session is created.

So I suspect something's wrong with the management of the HttpConfigOptions in ibm-watson, that was ok in java-sdk. Could you please advice?

Thank you Regards

kevinkowa commented 3 years ago

Can you send the stack trace for 9.0.2?

kevinkowa commented 3 years ago

One suggestion that I can give out right now is to check if you have the right certificates

lwittenb commented 3 years ago

Here is the full stack trace:

2021-02-16 12:17:33,337 [main] ERROR [DialogManager] (DialogManager.java:210) Error while fetching access token from token service: 
2021-02-16 12:17:33,339 [main] INFO  [DialogManager] (DialogManager.java:211) java.lang.RuntimeException: Error while fetching access token from token service: 
    at com.ibm.cloud.sdk.core.security.TokenRequestBasedAuthenticator.getToken(TokenRequestBasedAuthenticator.java:256)
    at com.ibm.cloud.sdk.core.security.TokenRequestBasedAuthenticator.authenticate(TokenRequestBasedAuthenticator.java:194)
    at com.ibm.cloud.sdk.core.service.BaseService.setAuthentication(BaseService.java:280)
    at com.ibm.cloud.sdk.core.service.BaseService.createCall(BaseService.java:218)
    at com.ibm.cloud.sdk.core.service.BaseService.createServiceCall(BaseService.java:256)
    at com.ibm.watson.assistant.v2.Assistant.createSession(Assistant.java:171)
    at mypackage.watson.business.DialogManager.main(DialogManager.java:183)
Caused by: java.net.ConnectException: Failed to connect to iam.cloud.ibm.com/95.100.59.118:443
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:265)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:183)
    at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224)
    at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:108)
    at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:88)
    at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229)
    at okhttp3.RealCall.execute(RealCall.java:81)
    at com.ibm.cloud.sdk.core.security.TokenRequestBasedAuthenticator$2.run(TokenRequestBasedAuthenticator.java:312)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.net.ConnectException: Connection timed out: connect
    at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at okhttp3.internal.platform.Platform.connectSocket(Platform.java:130)
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:263)
    ... 20 more

DialogManager is my class and line 183 is the following:

if (null != assistant)
{
    ServiceCall<SessionResponse> serviceCall = assistant.createSession(options);

What certificates are you referring to? I don't have any... :(

lwittenb commented 3 years ago

Ok, so having again a look at the stacktrace, what is this additional endpoint? Caused by: java.net.ConnectException: Failed to connect to iam.cloud.ibm.com/95.100.59.118:443

I am being told that the only URL pattern in the proxy whitelist is: https://*.watson.cloud.ibm.com/*

Because of the service endpoint we're using: https://api.eu-de.assistant.watson.cloud.ibm.com

So far it was enough with the java-sdk... but something changed since v7.0.0 with the IamAuthenticator, right? I did not find any mention of this in the documentation though... only endpoints mentioned are the api ones.

kevinkowa commented 3 years ago

@lwittenb Okay so here's what could be happening, in the older version of the SDK the proxy would affect the authenticator when you called the functionconfigureClient. So, the proxy would be used to grab your credentials by going to the iam.cloud.ibm.com site. Now, in newer versions of the SDK, you have to add a proxy for the authenticator too. In other words, configureClient doesn't set a proxy for the authenticator anymore.

So here you have two choices:

  1. Setting the properties like you did.
  2. Setting a proxy for the authenticator.

Can you try doing this

      Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
      IamAuthenticator authenticator = new IamAuthenticator(apiKey);
      authenticator.setProxy(proxy);

      Assistant assistant = new Assistant("2019-04-30", authenticator);

      HttpConfigOptions httpOptions = new HttpConfigOptions.Builder()
              .proxy(proxy)
              .loggingLevel(HttpConfigOptions.LoggingLevel.BASIC)
              .build();
      assistant.configureClient(httpOptions);
lwittenb commented 3 years ago

Aaaah perfect, setting the proxy for the authenticator solved my issue. I had no idea it was also necessary for that, as I did not find any related note either in the readme.md or in the apidocs.

Thanks a lot anyway. Cheers