HttpsUrlConnection EOFException

Я использую следующий код для почтовых запросов

public String executeHttpPost(String uri, String data) {

    HttpURLConnection conn = null;
    URL url = null;
    String s = null;

    try {

        url = new URL(uri);

         conn = (HttpURLConnection) url.openConnection();

        if (conn instanceof HttpsURLConnection) {
            Log.d("HTTPS", "HttpsUrl");
        }

        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
        wr.write(data);
        wr.flush();

        DisplayResponseHeaders(conn);

        s = readStream(conn.getInputStream());
        Log.d("body", s);

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        s = null;
        e.printStackTrace();
    } finally {
        conn.disconnect();
    }
    return s;
  }

Когда я использую его через http, все работает хорошо, но через https я получаю

 java.io.EOFException
 at libcore.io.Streams.readAsciiLine(Streams.java:203)
 at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:573)
 at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:821)
 at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:283)
 libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
 libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
 ru.fors.remsmed.fragment.NetHelper.executeHttpPost(NetHelper.java:234)
 ru.fors.remsmed.LoginActivity$UserLoginTask.doInBackground(LoginActivity.java:424)
 ru.fors.remsmed.LoginActivity$UserLoginTask.doInBackground(LoginActivity.java:1)
 android.os.AsyncTask$2.call(AsyncTask.java:287)
 java.util.concurrent.FutureTask.run(FutureTask.java:234)
 android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
 java.lang.Thread.run(Thread.java:856)
 threadid=14: thread exiting with uncaught exception (group=0x40a71930)

Я обнаружил проблему с ошибкой повторного использования соединений в httpsurlconnection и возможным решением:

if (Build.VERSION.SDK != null && Build.VERSION.SDK_INT > 13) {
   conn.setRequestProperty("Connection", "close");
}

Но это не работа для меня.


person Valeriy    schedule 12.02.2014    source источник


Ответы (2)


Попробуйте изменить свой код следующим образом:

    conn.setRequestProperty("Content-Type",
                            "application/x-www-form-urlencoded; charset: UTF-8");
    byte[] output = data.getBytes("UTF-8");
    conn.setFixedLengthStreamingMode(output.length);

    os = conn.getOutputStream();
    os.write(output);
    os.flush();
    os.close();

    DisplayResponseHeaders(conn);

    if (conn.getResponseCode() == 200) { // or other 2xx code like 204

        s = readStream(conn.getInputStream());
        Log.d("body", s);
    }
    else {

        // handle error conditions like 404, 400, 500, ...

        // now it may be necessary to read the error stream

        InputStream errorStream = conn.getErrorStream();

        // ...
    }

Насколько я знаю, вы всегда должны закрывать все потоки, которые вы открыли. Я не уверен, что conn.disconnect() делает это за вас.

Если вы хотите более удобно кодировать свои запросы HTTP(S), вы можете взглянуть на DavidWebb где у вас есть список библиотек, которые помогут вам избежать использования громоздкого HttpURLConnection.

person hgoebl    schedule 12.02.2014

Это исключение EOFException предполагает, что ответ искажен — возможно, в нем отсутствует пустая строка после заголовков. Некоторый клиентский код HTTP в этом случае более снисходителен, для меня iOS могла нормально обрабатывать ответы моего сервера, но я получал EOFException на Android, используя HttpURLConnection.

Мой сервер использовал python SimpleHTTPServer, и я ошибочно предполагал, что все, что мне нужно было сделать, чтобы указать на успех, было следующим:

self.send_response(200)

Это отправляет строку заголовка исходного ответа, сервер и заголовок даты, но оставляет поток в состоянии, в котором вы также можете отправлять дополнительные заголовки. HTTP требует дополнительной новой строки после заголовков, чтобы указать, что они завершены. Похоже, что если эта новая строка отсутствует, когда вы пытаетесь получить тело результата InputStream или код ответа и т. д. с помощью HttpURLConnection, тогда она генерирует исключение EOFException (что на самом деле разумно, если подумать). Некоторые HTTP-клиенты приняли короткий ответ и сообщили об успешном результате, что, возможно, привело к тому, что я несправедливо указал пальцем на HttpURLConnection.

Вместо этого я изменил свой сервер, чтобы сделать это:

self.send_response(200)
self.send_header("Content-Length", "0")
self.end_headers()

Больше никаких EOFException с этим кодом. Возможно, решения «Соединение: закрыть» вызывают некоторое поведение на определенных серверах, которые могут обойти это (например, убедиться, что ответ действителен перед закрытием), но это не так с python SimpleHTTPServer, и основная причина оказалась моя вина.

NB: в Android до Froyo (2.2) есть некоторые ошибки, связанные с соединениями поддержания активности — см. сообщение в блоге здесь: http://android-developers.blogspot.co.uk/2011/09/androids-http-clients.html. Я еще не видел убедительных доказательств ошибок в новых версиях Android.

person tangobravo    schedule 08.01.2015