Подтвердить что ты не робот

HttpsUrlСоединение и поддержка

Я использую com.sun.net.httpserver.HttpsServer в моем текущем проекте, который занимается аутентификацией клиента и т.д. В настоящее время он только распечатывает адрес/порт клиентов, так что я могу проверить, используется ли одно TCP-соединение для нескольких запросов (keep-alive) или если для каждого запроса установлено новое соединение (и, таким образом, каждое новое SSL-рукопожатие делается каждый раз). Когда я использую FireFox, чтобы сделать несколько запросов к серверу, я вижу, что keep-alive работает. Таким образом, часть сервера отлично работает с GET и POST-запросами.

Если я использую HttpURLConnection для запроса на сервер (в этом случае используется no SSL), то работает также: keep-alive: только одно соединение установлено для нескольких последовательно запущенных запросов.

Но если я использую HttpsURLConnection (используя точно такой же код, но используя SSL), то keep-alive больше не работает. Поэтому для каждого запроса установлено новое соединение, хотя я использую те же SSLContextSSLSocketFactory):

// URL myUrl = ...
// SSLContext mySsl = ...
HttpsURLConnection conn = (HttpsURLConnection) myUrl.openConnection();
conn.setUseCaches(false);
conn.setSSLSocketFactory(mySsl.getSocketFactory());

conn.setRequestMethod("POST");
// send Data
// receive Data

Как заставить HttpsURLConnection использовать keep-alive, потому что многие запросы приведут к большому количеству SSL-рукопожатий, что является реальной проблемой производительности?

Обновление (2012-04-02): Вместо вызова mySsl.getSocketFactory() каждый раз я пытался кэшировать SSLSocketFactory. Но ничего не изменилось. Проблема все еще существует.

4b9b3361

Ответ 1

Я не мог заставить его работать с HttpsUrlConnection. Но клиент HTTP Apache очень хорошо поддерживает соединение SSL.

Ответ 2

Я столкнулся с этой же проблемой и, наконец, получил решение после некоторой углубленной отладки.

Http UrlConnection действительно поддерживает Keep-Alive по умолчанию, но сокеты должны быть в очень специфическом состоянии для повторного использования.

Это:

  • Входные потоки должны полностью потребляться. Вы должны вызывать чтение во входном потоке, пока он не вернет -1, а также закройте его.
  • Настройки в базовом сокете должны использовать одни и те же объекты.
  • Вы должны вызвать disconnect (да, это контр-интуитивно понятен) в URL-соединении URL-адресов Http (-ов), когда это делается.

В приведенном выше коде проблема:

conn.setSSLSocketFactory(mySsl.getSocketFactory());

Сохранение результата getSocketFactory() для статической переменной во время инициализации, а затем передача этого значения в conn.setSSLSocketFactory должна позволить повторному использованию сокета.

Ответ 3

Установление соединения SSL действительно дорого или для служебных вызовов, или при получении большого количества ресурсов из браузера.

Java Http(s)UrlConnection обрабатывает HTTP (S) Keep-Alive по умолчанию.

Я не нашел исходный код default SSLSocketFactory и, вероятно, там реализован механизм keep-alive. В качестве подтверждения отключите собственную реализацию SSLSocketFactory для теста, с настраиваемым хранилищем доверия в javax.net.ssl.trustStore, чтобы ваш самозаверяющий сертификат был принят.

В соответствии с OpenJDK 7 ServerImpl реализация, которая использует ServerConfig the HttpsServer, который вы использовали, генерирует постоянный доступ с 5-минутным таймаутом по умолчанию.

Я предлагаю вам установить свойство sun.net.httpserver.debug на true server-side, чтобы получить подробную информацию.

Позаботьтесь, чтобы ваш код не добавлял заголовок Connection: close, который отключает механизм keep-alive.

Ответ 4

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

con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");

Ответ 5

Насколько я понимаю HTTP/1.1 и HTTPS, также документально подтвержденный здесь, Keep-Alive не является сквозным заголовком, а заголовком hop-to-hop, Поскольку SSL включает в себя несколько шагов установления связи между "разными перелетами" (например, CA и сервером) для каждого нового соединения, я думаю, что Keep-Alive может не применяться в контексте SSL. Итак, вот почему Keep-Alive заголовок игнорируется с использованием соединений HTTPS. Исходя из этого этого вопроса, вам может потребоваться убедиться, что один экземпляр HTTP-соединения используется для обеспечения наблюдения Keep-Alive. Кроме того, в этом вопросе кажется, что Apache HTTPClient является лучшим решением.

Ответ 6

Мы можем настроить веб-сервер Apache, добавить следующие директивы, чтобы узнать, поддерживает ли Apache access.log соединение keep-alive для http-клиента.

LogFormat "%k %v %h %l %u %t \"%r\" %>s %b" common
CustomLog "logs/access.log" common 

http://httpd.apache.org/docs/current/mod/mod_log_config.html

"% k" Количество запросов на сохранение, обрабатываемых в этом соединении. Интересно, используется ли KeepAlive, так что, например, "1" означает первый запрос на сохранение после первоначального, "2" - второй и т.д.; в противном случае это всегда 0 (с указанием начального запроса).

Ответ 7

Я столкнулся с той же проблемой, и Билл Хили прав. Я проверил свой пример кода ниже с несколькими https-библиотеками. HttpsURLConnection и OKHTTP - это то же самое поведение. Волейбол немного отличается, когда возобновляется сессия, но почти такое же поведение. Надеюсь, это поможет.

public class SampleActivity extends Activity implements OnClickListener {

    // Keep default context and factory
    private SSLContext mDefaultSslContext;
    private SSLSocketFactory mDefaultSslFactory;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        findViewById(R.id.button_id).setOnClickListener(this);

        try {
            // Initialize context and factory
            mDefaultSslContext = SSLContext.getInstance("TLS");
            mDefaultSslContext.init(null, null, null);
            mDefaultSslFactory = mDefaultSslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            Log.e(TAG, e.getMessage(), e);
        }

    }

    @Override
    public void onClick(View v){
        SSLContext sslcontext;
        SSLSocketFactory sslfactory;

        try {
            // If using this factory, enable Keep-Alive
            sslfactory = mDefaultSslFactory;

            // If using this factory, enable session resumption (abbreviated handshake)
            sslfactory = mDefaultSslContext.getSocketFactory();

            // If using this factory, enable full handshake each time
            sslcontext = SSLContext.getInstance("TLS");
            sslcontext.init(null, null, null);
            sslfactory = sslcontext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            Log.e(TAG, e.getMessage(), e);
        }

        URL url = new URL("https://example.com");
        HttpsURLConnection = conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(sslfactory);
        conn.connect();
    }
}

Обновление:

Совместное использование SSLSocketFactory позволяет сохранить работоспособность. Совместное использование SSLContext и получение факторизации каждого запроса позволяют возобновить сеанс. Я не знаю, как работает TLS-стек, но только подтвердил это поведение соединения с некоторыми мобильными устройствами.

Если вы хотите включить keep-alive среди нескольких классов, вы должны поделиться экземпляром SSLSocketFactory, используя singleton pattern.

Если вы хотите включить возобновление сеанса, убедитесь, что параметры таймаута сеанса достаточно длинные на стороне сервера, такие как SSLSessionCacheTimeout (apache), ssl_session_timeout (nginx).