square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.08k stars 7.31k forks source link

1.4.0 New line in String causes retrofit.RetrofitError: java.io.EOFException #397

Closed mazurio closed 10 years ago

mazurio commented 10 years ago

Gist: https://gist.github.com/mazurio/8750846

Description is a text taken from TextView (Android), if it contains more than one new line then it causes EOFException and insert fails. Trying to figure it out now.

I tried to escape the string. It is the same problem.

thib-rdr commented 9 years ago

@grub- Here are my versions :

compile 'com.squareup.retrofit:retrofit:1.7.1'
compile 'com.squareup.okhttp:okhttp:2.0.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
johnjohndoe commented 9 years ago

I can reproduce the error for every POST request I send. Here is my project setup. Please let me know if I can add any further information.

app/build.gradle:

compile 'com.squareup.dagger:dagger:1.2.2'
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
compile 'com.squareup.retrofit:retrofit:1.8.0'
compile 'com.squareup.retrofit:converter-jackson:1.8.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
compile 'com.fasterxml.jackson.core:jackson-databind:2.5.0'
compile 'io.reactivex:rxandroid:0.23.0'

ReportsService.java:

public interface ReportsService {
    @POST("/")
    public Observable<CreateReportResponse> createReport(
            @Body Report report
    );
}

ApiModule.java:

public final class ApiModule {

    public static final String API_URL = "http://100.200.100.200:8000";
    public static final String REPORTS = "REPORTS";

    @Provides
    @Singleton
    @Named(REPORTS)
    Endpoint provideReportsEndpoint() {
        return Endpoints.newFixedEndpoint(API_URL);
    }

    @Provides
    @Singleton
    ObjectMapper provideObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setPropertyNamingStrategy(
                PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
        return objectMapper;
    }

    @Provides
    @Singleton
    @Named(REPORTS)
    ReportsService provideReportsService(
            @Named(REPORTS) Endpoint endpoint,
            ObjectMapper objectMapper) {
        return createRestAdapterBuilder(objectMapper)
                .setEndpoint(endpoint)
                .build()
                .create(ReportsService.class);
    }

    private RestAdapter.Builder createRestAdapterBuilder(ObjectMapper objectMapper) {
        return new RestAdapter.Builder()
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setConverter(new JacksonConverter(objectMapper))
                .setRequestInterceptor(request -> {
                    request.addHeader("Accept", "application/json");
                });
    }

}

Report.java:

public class Report {

    public String userName;
    public String emailAddress;

    public Report(String userName, String emailAddress) {
        this.userName = userName;
        this.emailAddress = emailAddress;
    }
}

fragment_new_report.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <EditText
            android:id="@+id/new_report_user_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_user_name"
            android:layout_gravity="center_horizontal" />

        <EditText
            android:id="@+id/new_report_email_address"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_email_address"
            android:layout_gravity="center_horizontal" />

    </LinearLayout>
</ScrollView>

NewReportFragment:

protected void onSubmitReport() {
    Editable userName = userNameEditText.getText();
    Editable emailAddress = emailAddressEditText.getText();
    Report report = new Report(
            userName.toString(),
            emailAddress.toString());
    createReport(report);
}

protected void createReport(final Report Report) {

    Observable<CreateReportResponse> responseObservable =
            mReportsService.createReport(report);
    AndroidObservable.bindFragment(this, responseObservable)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<CreateReportResponse>() {
                @Override
                public void onCompleted() {
                    Log.d(getClass().getName(), "Completed createReport");
                }

                @Override
                public void onError(Throwable e) {
                    Log.e(getClass().getName(), e.getMessage());
                }

                @Override
                public void onNext(CreateReportResponse createReportResponse) {
                    Log.d(getClass().getName(), createReportResponse.toString());
                }
            });
}

This produces the following an EOFException on the client:

              Retrofit  D  ---> HTTP POST http://100.200.100.200:8000/
                        D  Accept: application/json
                        D  Content-Type: application/json; charset=UTF-8
                        D  Content-Length: 64
                        D  {"email_address":"test@test.com","user_name":"John"}
                        D  ---> END HTTP (64-byte body)
                        D  ---- ERROR http://100.200.100.200:8000/
                        D  java.io.EOFException: \n not found: size=0 content=...
                        D      at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:200)
                        D      at com.squareup.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:187)
                        D      at com.squareup.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80)
                        D      at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:791)
                        D      at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:678)
                        D      at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:431)
                        D      at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:376)
                        D      at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:471)
                        D      at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
                        D      at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
                        D      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
                        D      at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
                        D      at retrofit.RestAdapter$RestHandler$1.invoke(RestAdapter.java:265)
                        D      at retrofit.RxSupport$2.run(RxSupport.java:55)
                        D      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
                        D      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                        D      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
                        D      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
                        D      at retrofit.Platform$Android$2$1.run(Platform.java:142)
                        D      at java.lang.Thread.run(Thread.java:841)
                        D  ---- END ERROR
ts.NewReportFragment$1  E  \n not found: size=0 content=...

When I remove OkHttp from build.gradle the following is logged:

                D  ---> HTTP POST http://100.200.100.200:8000/
                D  Accept: application/json
                D  Content-Type: application/json; charset=UTF-8
                D  Content-Length: 67
                D  {"email_address":"test@test.com","user_name":"John"}
                D  ---> END HTTP (67-byte body)
                D  ---- ERROR http://100.200.100.200:8000/
                D  java.io.EOFException
                D      at com.android.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:95)
                D      at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:175)
                D      at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:101)
                D      at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:616)
                D      at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:379)
                D      at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:323)
                D      at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:491)
                D      at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
                D      at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
                D      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
                D      at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
                D      at retrofit.RestAdapter$RestHandler$1.invoke(RestAdapter.java:265)
                D      at retrofit.RxSupport$2.run(RxSupport.java:55)
                D      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
                D      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                D      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
                D      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
                D      at retrofit.Platform$Android$2$1.run(Platform.java:142)
                D      at java.lang.Thread.run(Thread.java:818)
                D  ---- END ERROR

AndroidRuntime  E  FATAL EXCEPTION: main
                E  java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
                E      at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:52)
                E      at android.os.Handler.handleCallback(Handler.java:739)
                E      at android.os.Handler.dispatchMessage(Handler.java:95)
                E      at android.os.Looper.loop(Looper.java:135)
                E      at android.app.ActivityThread.main(ActivityThread.java:5221)
                E      at java.lang.reflect.Method.invoke(Native Method)
                E      at java.lang.reflect.Method.invoke(Method.java:372)
                E      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
                E      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
                E  Caused by: rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
                E      at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:174)
                E      at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:96)
                E      at rx.internal.operators.NotificationLite.accept(NotificationLite.java:147)
                E      at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:177)
                E      at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.access$000(OperatorObserveOn.java:65)
                E      at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:153)
                E      at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:45)
                E      ... 8 more
                E  Caused by: rx.exceptions.CompositeException: 2 exceptions occurred.
                E      ... 15 more
                E  Caused by: rx.exceptions.CompositeException$CompositeExceptionCausalChain: Chain of Causes for CompositeException In Order Received =>
                E      at android.util.Log.getStackTraceString(Log.java:330)
                E      at com.android.internal.os.RuntimeInit.Clog_e(RuntimeInit.java:59)
                E      at com.android.internal.os.RuntimeInit.access$200(RuntimeInit.java:43)
                E      at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:85)
                E      at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693)
                E      at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690)
                E      at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:56)
                E      ... 8 more
                E  Caused by: retrofit.RetrofitError
                E      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:390)
                E      at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
                E      at retrofit.RestAdapter$RestHandler$1.invoke(RestAdapter.java:265)
                E      at retrofit.RxSupport$2.run(RxSupport.java:55)
                E      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
                E      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                E      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
                E      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
                E      at retrofit.Platform$Android$2$1.run(Platform.java:142)
                E      at java.lang.Thread.run(Thread.java:818)
                E  Caused by: java.io.EOFException
                E      at com.android.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:95)
                E      at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:175)
                E      at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:101)
                E      at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:616)
                E      at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:379)
                E      at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:323)
                E      at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:491)
                E      at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
                E      at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
                E      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
                E      ... 9 more

I also removed all configuration such as android:singleLine="true" and android:inputType="..." from the EditText to eliminate any side effects.


I was able to reproduce this on a Nexus 5 (Android 5.0.1), Moto G 2nd Edition (Android 4.4.4), Samsung Galaxy S4 (Android 4.4.2).


I use this SimpleHTTPServer to test the POST requests. The server receives the following:

Accept: application/json
Content-Type: application/json; charset=UTF-8
Content-Length: 65
Host: 100.200.100.200:8000
Connection: Keep-Alive
Accept-Encoding: gzip

FieldStorage(None, None, '{"email_address":"test@test.com","user_name":"John"}')
swankjesse commented 9 years ago

Can you reproduce this reliably? What does your server return?

swankjesse commented 9 years ago

@johnjohndoe your simplehttpserver.py program is broken, and OkHttp is correct to fail on responses returned from it. You can see it by hitting the server with curl, which will also fail.

$ curl --data '{}' http://localhost:8000
curl: (52) Empty reply from server
johnjohndoe commented 9 years ago

Oh, my fault - sorry. I just picked it up yesterday. I also tried curl but missed to notice the error.

Can you recommend another simple server which allows quick testing of HTTP requests?

swankjesse commented 9 years ago

@johnjohndoe MockWebServer.

xuxucode commented 9 years ago

I got this issue sometimes on Andorid 2.3.4.

interface:

@Headers("X-API-Version: v1.0")
@POST("/arts")
void addArt(@Body ArtProduct art, Callback<ArtResource> callback);

gist: https://gist.github.com/edwardaa/443bb10abf2b812a9363

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.+'
    compile 'com.android.support:cardview-v7:21.0.+'
    compile 'com.android.support:recyclerview-v7:21.0.+'
    compile 'com.google.code.gson:gson:2.3.1'
    compile 'com.squareup.okhttp:okhttp:2.1.0'
    compile 'com.squareup.okhttp:okhttp-urlconnection:2.1.0'
    compile 'com.squareup:otto:1.3.5'
    compile 'com.squareup.picasso:picasso:2.4.0'
    compile 'com.squareup.retrofit:retrofit:1.8.0'
    compile 'com.jakewharton:butterknife:6.0.0'
    compile 'io.reactivex:rxandroid:0.23.0'
    compile 'com.github.tony19:logback-android-core:1.1.1-3'
    compile 'com.github.tony19:logback-android-classic:1.1.1-3'https://gist.github.com/edwardaa/443bb10abf2b812a9363]
    compile 'org.slf4j:slf4j-api:1.7.6'
}
johnjohndoe commented 9 years ago

@swankjesse Thanks for the hint - I will come back if I still experience problems. (I am still fighting to set MockWebServer up in my project.)

JakeWharton commented 9 years ago

In order to reduce the split-brain problem this bug is causing I am locking this issue version in favor of OkHttp's @ https://github.com/square/okhttp/issues/1114.