HttpsUrlConnection et keep-alive

j'utilise com.sun.net.httpserver.HttpsServer dans mon projet actuel qui traite de l'authentification des clients, etc.. Actuellement, il n'imprime que l'adresse/le port du client, de sorte que je puisse vérifier si une connexion TCP est utilisée pour des requêtes multiples ( keep-alive ) ou si une nouvelle connexion est établie pour chaque requête (et donc une nouvelle poignée de main SSL est faite à chaque fois). Lorsque J'utilise FireFox pour faire plusieurs requêtes contre le serveur, je peux voir que keep-alive fonctionne. Donc la partie serveur fonctionne très bien avec Obtenir et POST-demandes.

si j'utilise HttpURLConnection pour faire une requête contre le serveur (dans ce cas en utilisant Non SSL) keep-alive fonctionne aussi: une seule connexion est établie pour plusieurs requêtes démarrées séquentiellement.

mais si j'utilise HttpsURLConnection (en utilisant exactement le même code, mais en utilisant SSL) alors keep-alive ne fonctionne plus. Donc, pour chaque demande, une nouvelle connexion est établie, bien que j'utilise le même SSLContext (et SSLSocketFactory ):

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

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

Comment puis-je forcer HttpsURLConnection à utiliser keep-alive parce que de nombreuses requêtes mèneront à de nombreuses poignées de main SSL ce qui est un véritable problème de performance?

mise à jour (2012-04-02): Au lieu d'appeler mySsl.getSocketFactory() à chaque fois, j'ai essayé de mettre en cache le SSLSocketFactory . Mais rien n'a changé. Le problème existe toujours.

24
demandé sur Biggie 2012-03-30 16:51:48

7 réponses

j'ai rencontré exactement le même problème et avoir enfin une solution après quelques approfondie de débogage.

Http (s) UrlConnection ne gère pas Keep-Alive par défaut, mais les sockets doivent être dans un état très spécifique pour être réutilisés.

ce sont:

  • les flux D'entrée doivent être entièrement consommés. Vous devez appeler read sur le flux d'entrée jusqu'à ce qu'il retourne -1 et aussi le fermer.
  • sur le socket sous-jacente doit utiliser exactement les mêmes objets.
  • vous devez appeler déconnect (Oui c'est contre-intuitif) sur la connexion url Http(s)quand vous en avez terminé avec elle.

dans le code ci-dessus, le problème est:

conn.setSSLSocketFactory(mySsl.getSocketFactory());

sauvegarde du résultat de getSocketFactory() vers une variable statique lors de l'initialisation, puis passage de celle-ci à conn.setSSLSocketFactory doit permettre la réutilisation de la socket.

17
répondu Bill Healey 2015-04-09 19:01:47

Je n'ai pas pu le faire fonctionner avec HttpsUrlConnection . Mais le client HTTP D'Apache gère très bien keep-alive avec des connexions SSL.

5
répondu Biggie 2016-09-23 11:21:54

établissement de connexion SSL est vraiment cher soit pour les appels de service ou lors de l'obtention de nombreuses ressources à partir d'un navigateur.

Java Http(s)UrlConnection poignées HTTP(S) Keep-Alive par défaut .

Je n'ai pas trouvé le code source du default SSLSocketFactory et probablement le mécanisme keep-alive y est implémenté. Comme confirmation, désactivez votre propre implémentation SSLSocketFactory pour un test, avec un custom trust store dans javax.net.ssl.trustStore de sorte que votre certificat auto-signé est accepté.

, Selon OpenJDK 7 ServerImpl "1519110920 la mise en œuvre" qui utilise ServerConfig le HttpsServer vous avez utilisé émet un keep-alive 5 minutes délai d'attente par défaut.

je vous propose de définir la propriété sun.net.httpserver.debug à true côté serveur pour obtenir des détails.

prenez soin de votre le code n'ajoute pas l'en-tête Connection: close qui désactive le mécanisme keep-alive.

2
répondu Yves Martin 2017-08-04 16:31:32

essayez d'ajouter le code suivant:

con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");
1
répondu UVM 2012-04-09 07:12:52

autant que je puisse comprendre HTTP/1.1 et protocole HTTPS , également documenté ici , Keep-Alive n'est pas un en-tête de bout en bout mais un en-tête hop-to-hop . Puisque SSL implique de multiples étapes de handshaking entre " différents hops "(par exemple CA et le serveur) pour chaque nouvelle connexion, je pense que Keep-Alive peut ne pas être applicable dans le contexte SSL. Donc, que c'est peut-être pourquoi l'en-tête Keep-Alive est ignoré en utilisant les connexions HTTPS. Sur la base de cette cette question , vous devrez peut-être vous assurer que une instance de connexion HTTP est utilisée pour garantir l'observation Keep-Alive . En outre, dans la question, il semble que Apache HTTPClient a été une meilleure solution.

0
répondu nobeh 2017-05-23 12:26:06

nous pouvons configurer un serveur web Apache, ajouter les directives suivantes pour voir si L'accès Apache.journal a une connexion persistante pour le client 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 " Nombre de requêtes keepalive traitées sur cette connexion. Intéressant si KeepAlive est utilisé, de sorte que, par exemple, un '1' signifie la première requête keepalive après initiale, '2' le deuxième, etc...; sinon, c'est toujours 0 (indiquant la requête initiale).

0
répondu WENPIN 2014-09-23 09:00:10

J'ai fait face au même problème, et Bill Healey a raison. J'ai testé mon code d'exemple ci-dessous avec quelques https bibliothèques. HttpsURLConnection et OKHTTP ont exactement le même comportement. Volley est un peu différent lors de la reprise de session, mais presque le même comportement. J'espère que ce sera de l'aide.

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();
    }
}

mise à jour:

partager SSLSocketFactory permet de rester en vie. Partager SSLContext et obtenir facotry chaque requête activent la reprise de la session. Je n'ai pas savoir comment fonctionne la pile TLS, mais vient de confirmer ces comportements de connexion avec certains appareils mobiles.

si vous voulez activer keep-alive parmi plusieurs classes, vous devez partager L'instance de SSLSocketFactory en utilisant singleton pattern.

si vous voulez activer la reprise de session, assurez-vous que les paramètres de temporisation de session sont suffisamment longs du côté du serveur, tels que SSLSessionCacheTimeout (apache), ssl_session_timeout (nginx).

0
répondu forte916 2017-03-24 15:07:20