rburgst / okhttp-digest

a digest authenticator for okhttp
Apache License 2.0
191 stars 42 forks source link

java.lang.NoSuchFieldError #75

Closed zipswich closed 2 years ago

zipswich commented 3 years ago

It worked fine for a long time until I switched to io.github.rburgst:okhttp-digest:2.5 from jcenter recently.

The Android app crashes with the following error despite the code is in a try-catch block.

java.lang.NoSuchFieldError: 
  at com.burgstaller.okhttp.digest.DigestAuthenticator.authenticateWithState (DigestAuthenticator.java:242)
  at com.burgstaller.okhttp.digest.DigestAuthenticator.authenticate (DigestAuthenticator.java:180)
  at com.burgstaller.okhttp.DispatchingAuthenticator.authenticate (DispatchingAuthenticator.java:45)
  at com.burgstaller.okhttp.CachingAuthenticatorDecorator.authenticate (CachingAuthenticatorDecorator.java:35)
  at okhttp3.internal.http.RetryAndFollowUpInterceptor.followUpRequest (RetryAndFollowUpInterceptor.java:292)
  at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept (RetryAndFollowUpInterceptor.java:160)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:121)
  at com.burgstaller.okhttp.AuthenticationCacheInterceptor.intercept (AuthenticationCacheInterceptor.java:49)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.java:121)
  at okhttp3.RealCall.getResponseWithInterceptorChain (RealCall.java:254)
  at okhttp3.RealCall.execute (RealCall.java:92)
rburgst commented 3 years ago

I am fairly certain the problem stems from upgrading okhttp. I need to check what has changed in the latest version. For now you can work around by downgrading to okhttp 4.7.2

rburgst commented 3 years ago

hmm, ran all tests against okhttp 4.9.1 and everything passed. Which okhttp version are you using?

zipswich commented 3 years ago

@rburgst Thanks a lot for looking into it. First, I am fairly sure at this point the problem has been resolved by downgrading to io.github.rburgst:okhttp-digest:1.21 from io.github.rburgst:okhttp-digest:2.5 I saw crash reports from Google Play within a few hours after two app releases with io.github.rburgst:okhttp-digest:2.5. The last update using 1.21 was rolled out 16 hours ago and I have not seen a single crash. I will report back if the crash happens. This makes sense because upgrading to 2.5 was the major change after I got rid of jcenter() and started using mavenCentral() exclusively.

I am using api 'com.squareup.okhttp3:okhttp:4.7.2' I do not know why Android Studio does not prompt me to upgrade okhttp. It usually does when there is a newer version. For example, it now suggests upgrading okhttp-digest:1.21 to 2.5.

I am unable to reproduce this crash. I do not know what circumstances induce this. Here is the code:


final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
OkHttpClient client;
if (sUser == null) {
    client = builder
            .addInterceptor(new AuthenticationCacheInterceptor(authCache))
            .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
            .hostnameVerifier((hostname, session) -> true)
            .retryOnConnectionFailure(true)
            .build();
} else {
    Credentials credentials = new Credentials(sUser, sPassword);
    final BasicAuthenticator basicAuthenticator = new BasicAuthenticator(credentials);
    final DigestAuthenticator digestAuthenticator = new DigestAuthenticator(credentials);
    DispatchingAuthenticator authenticator = new DispatchingAuthenticator.Builder()
            .with("digest", digestAuthenticator)
            .with("basic", basicAuthenticator)
            .build();
    client = builder
            .authenticator(new CachingAuthenticatorDecorator(authenticator, authCache))
            .addInterceptor(new AuthenticationCacheInterceptor(authCache))
            .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
            .hostnameVerifier((hostname, session) -> true)
            .retryOnConnectionFailure(true)
            .build();
}
Request request = new Request.Builder()
        .url(sHTTPURL)
        .get()
        .build();
Response response = client.newCall(request).execute();

The crash is caused by the last line: Response response = client.newCall(request).execute();

rburgst commented 3 years ago

why are you using the api scope rather than implementation?

zipswich commented 3 years ago

@rburgst This is in a utility library shared by many apps. I use api in the library generally.

rburgst commented 3 years ago

in that case the only thing that might cause this could be proguard?

zipswich commented 3 years ago

That crossed my mind too. I use the following in proguard-project.txt: -keep class com.burgstaller.** { *; } Any clue why V1.21 works fine?

rburgst commented 3 years ago
 -keep class com.burgstaller.** { *; }

wont help at all in this case since the missing method is in the okhttp code. Something like that: https://github.com/square/okhttp/blob/master/okhttp/src/main/resources/META-INF/proguard/okhttp3.pro

okhttp-digest 1 depends on okhttp 3 and therefore, this is a quite different code base (they were using java in 3.x and kotlin in 4.x).

zipswich commented 3 years ago

The app also has the following that I added a long ago when it started using okhttp3. I probably copied it from somewhere, but I do not remember.

#okhttp3
-keepattributes Signature
-keepattributes Annotation
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontwarn okio.**
rburgst commented 3 years ago

since you are now using okhttp4 this will probably no longer be correct

zipswich commented 3 years ago

Did you mean api 'com.squareup.okhttp3:okhttp:4.7.2' is okhttp4?

rburgst commented 3 years ago

actually I didnt check yesterday, I just knew that they changed the implmentation to kotlin and that might bring a few changes that need to be done for proguard.

Now that I checked your proguard files look allright.

I do have my own application using these rules (again, I have been using okhttp for a long time and therefore, my rules might not be)


# fix okio / okhttp
-dontwarn okio.**
-dontwarn java.nio.file.Files
-dontwarn java.nio.file.Path
-dontwarn java.nio.file.OpenOption
-dontwarn org.conscrypt.**
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Keep until > okhttp 4.7.2
# https://github.com/square/okhttp/issues/6092
-keepnames class okhttp3.OkHttpClient

Since you are not able to reproduce the crash (I am assuming you were using the emulator with the same SDK version where the crash happened). There is not much I can do to support you. You might need to bring this up with the okhttp guys.

The API that okhttp-digest is using is a public api and AFAICT its being used correctly. Also, you are the first one to report such an issue, therefore, I somehow doubt that okhttp digest is to blame.

zipswich commented 3 years ago

Thanks a lot for looking into it. I have tested on an emulator and on a physical device, but could not reproduce it. However, I saw quite a few crashes from Google's crash reports but it is still rare because the app is active on hundreds of thousands of devices. I will give it another shot to try to reproduce it by testing different scenarios that I can come up. Since io.github.rburgst:okhttp-digest:1.21 works well now, it is not an urgent issue at all.

lfarkas commented 2 years ago

we've got exactly the same problem:

java.io.IOException: canceled due to java.lang.NoSuchFieldError: INSTANCE
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:185) ~[okhttp-3.14.9.jar:na]
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) ~[okhttp-3.14.9.jar:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[na:na]
        at java.base/java.lang.Thread.run(Unknown Source) ~[na:na]
        Suppressed: java.lang.NoSuchFieldError: INSTANCE
                at com.burgstaller.okhttp.digest.DigestAuthenticator.authenticateWithState(DigestAuthenticator.java:242) ~[okhttp-digest-2.5.jar:na]
                at com.burgstaller.okhttp.digest.DigestAuthenticator.authenticate(DigestAuthenticator.java:180) ~[okhttp-digest-2.5.jar:na]
                at com.burgstaller.okhttp.DispatchingAuthenticator.authenticate(DispatchingAuthenticator.java:45) ~[okhttp-digest-2.5.jar:na]
                at com.burgstaller.okhttp.CachingAuthenticatorDecorator.authenticate(CachingAuthenticatorDecorator.java:35) ~[okhttp-digest-2.5.jar:na]
                at okhttp3.internal.http.RetryAndFollowUpInterceptor.followUpRequest(RetryAndFollowUpInterceptor.java:230) ~[okhttp-3.14.9.jar:na]
                at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:119) ~[okhttp-3.14.9.jar:na]
                at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar:na]
                at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) ~[okhttp-3.14.9.jar:na]
                at com.burgstaller.okhttp.AuthenticationCacheInterceptor.intercept(AuthenticationCacheInterceptor.java:49) ~[okhttp-digest-2.5.jar:na]
                at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar:na]
                at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) ~[okhttp-3.14.9.jar:na]
                at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229) ~[okhttp-3.14.9.jar:na]
                at okhttp3.RealCall$AsyncCall.execute(RealCall.java:172) ~[okhttp-3.14.9.jar:na]
                ... 4 common frames omitted

do you have any tip what can be the problem?

one important note that this code in a container within a spring boot service...

lfarkas commented 2 years ago

The API that okhttp-digest is using is a public api and AFAICT its being used correctly. Also, you are the first one to report such an issue, therefore, I somehow doubt that okhttp digest is to blame.

@rburgst so the above statement is not so true:-) as it can be seen from my trace it's in DigestAuthenticator.java:248 which line is: final String uri = RequestLine.INSTANCE.requestPath(request.url()); where the INSTANCE is from: import okhttp3.internal.http.RequestLine; and i assume internal not really public api... IMHO simple delete the INSTANCE from the code solve the problem....and a new release would be nice:-)

rburgst commented 2 years ago

Hi, their source is kotlin, and RequestLine is an object, this translates into RequestLine.INSTANCE in java.

lfarkas commented 2 years ago

thanks!