square / okhttp

Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
https://square.github.io/okhttp/
Apache License 2.0
45.81k stars 9.16k forks source link

HttpLoggingInterceptor causes stackoverflow #2962

Closed agustinsivoplas closed 7 years ago

agustinsivoplas commented 7 years ago

I have this class to create a service with retrofit2 + rxandroid + okhttp.

public class ServiceCreator {

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
    private static final int DEFAULT_TIMEOUT = 30; //seconds

    private static Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create();
    private static RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io());

    private static Retrofit.Builder builder = new Retrofit.Builder()
            .baseUrl(BuildConfig.REST_BASE_ENDPOINT)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(rxAdapter);

    public static <RESTService> RESTService createService(Class<RESTService> service, String userToken) {

        if (BuildConfig.REST_DEBUG) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            httpClient.addInterceptor(logging);
        }

        if (StringUtils.isNotBlank(userToken)) {
            httpClient.addInterceptor(chain -> {
                Request original = chain.request();

                Request request = original.newBuilder()
                        .header(Constants.AUTHORIZATION_HEADER, userToken)
                        .method(original.method(), original.body())
                        .build();
                return chain.proceed(request);
            });
        }

        httpClient.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        httpClient.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        httpClient.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

        Retrofit retrofit = builder.client(httpClient.build()).build();
        return retrofit.create(service);
    }

    public static Retrofit retrofit() {
        return builder.client(httpClient.build()).build();
    }
}

This code causes an stackoverflow error in android kitkat.


FATAL EXCEPTION: RxNewThreadScheduler-1 java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
   at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:59)
   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
   at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
   at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
   at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.StackOverflowError
   at org.apache.harmony.security.asn1.ASN1StringType.getDecodedObject(ASN1StringType.java:99)
   at org.apache.harmony.security.asn1.ASN1StringType.decode(ASN1StringType.java:90)
   at org.apache.harmony.security.asn1.ASN1Choice.decode(ASN1Choice.java:308)
   at org.apache.harmony.security.x501.AttributeTypeAndValue$1.decode(AttributeTypeAndValue.java:339)
   at org.apache.harmony.security.asn1.BerInputStream.readSequence(BerInputStream.java:554)
   at org.apache.harmony.security.asn1.DerInputStream.readSequence(DerInputStream.java:105)
   at org.apache.harmony.security.asn1.ASN1Sequence.decode(ASN1Sequence.java:40)
   at org.apache.harmony.security.asn1.BerInputStream.decodeValueCollection(BerInputStream.java:626)
   at org.apache.harmony.security.asn1.BerInputStream.readSetOf(BerInputStream.java:606)
   at org.apache.harmony.security.asn1.DerInputStream.readSetOf(DerInputStream.java:115)
   at org.apache.harmony.security.asn1.ASN1SetOf.decode(ASN1SetOf.java:40)
   at org.apache.harmony.security.asn1.BerInputStream.decodeValueCollection(BerInputStream.java:626)
   at org.apache.harmony.security.asn1.BerInputStream.readSequenceOf(BerInputStream.java:584)
   at org.apache.harmony.security.asn1.ASN1SequenceOf.decode(ASN1SequenceOf.java:40)
   at org.apache.harmony.security.asn1.ASN1Type.decode(ASN1Type.java:82)
   at javax.security.auth.x500.X500Principal.<init>(X500Principal.java:78)
   at com.android.org.conscrypt.OpenSSLX509Certificate.getIssuerX500Principal(OpenSSLX509Certificate.java:422)
   at com.android.org.conscrypt.OpenSSLX509Certificate.getIssuerDN(OpenSSLX509Certificate.java:236)
   at com.android.org.conscrypt.TrustManagerImpl.cleanupCertChainAndFindTrustAnchors(TrustManagerImpl.java:340)
   at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:225)
   at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:202)
   at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:611)
   at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
   at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:405)
   at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:241)
   at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:198)
   at okhttp3.internal.connection.RealConnection.buildConnection(RealConnection.java:174)
   at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:114)
   at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:193)
   at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:129)
   at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:98)
   at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
   at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
   at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
   at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:109)
   at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)

But if I remove this line of code, works perfect.

        if (BuildConfig.REST_DEBUG) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            httpClient.addInterceptor(logging);
        }

I have uses this class without RxAndroid and works perfect with this versions:

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

Nowadays I'm using:

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

Also I have created a stackoverflow question: http://stackoverflow.com/questions/40441957/retrofit-rxandroid-causes-stackoverflow-error-on-kitkat

Thanks

tachang commented 7 years ago

I wonder if this has to do with https://github.com/square/okhttp/issues/2961 as well.

I fixed it by setting the log level to headers.

            logging.setLevel(HttpLoggingInterceptor.Level.HEADER);
JakeWharton commented 7 years ago

Are you calling createService every time you need to make a request? Because you're adding the same interceptor over and over and over.

Please provide an executable test case.

agustinsivoplas commented 7 years ago

@JakeWharton You are right, I was adding the same interceptor over and over. Sorry

dibyendumr commented 7 years ago

Thanks a lot to @agustinsivoplas for the very rarely foudn solution to the one of most trivial problems on OkHttp and retrofit. Equally thanks to @tachang. I was struggling with a solution and in want of a solution very badly I found this article and to my surprise it worked.

As I found out if you remove

HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient.addInterceptor(logging);

OR as @tachang said to make the log level to: logging.setLevel(HttpLoggingInterceptor.Level.HEADER);

Thanks again to all...

agustinsivoplas commented 6 years ago

@dibyendumr Glad to help. Thank you for the great library.