web-dev-qa-db-fra.com

HttpsUrlConnection et garder en vie

J'utilise com.Sun.net.httpserver.HttpsServer dans mon projet actuel qui traite de l'authentification du client, etc. Actuellement, il n'imprime que l'adresse/le port du client, ce qui permet de vérifier si une connexion TCP est utilisée pour plusieurs requêtes (keep-alive) ou si une nouvelle connexion est utilisée. la connexion est établie pour chaque demande (et donc une nouvelle poignée de main SSL est faite à chaque fois). Lorsque j'utilise FireFox pour effectuer plusieurs requêtes sur le serveur, je constate que le maintien en activité fonctionne. La partie serveur fonctionne donc bien avec les requêtes GET et POST.

Si j'utilise HttpURLConnection pour effectuer une requête sur le serveur (dans ce cas, utilisez no SSL), keep-alive fonctionne également: une seule connexion est établie pour plusieurs demandes lancé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 la 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 forcer HttpsURLConnection à utiliser keep-alive car de nombreuses requêtes conduiront à de nombreuses liaisons SSL, ce qui constitue un réel problème de performances?

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

25
Biggie

Je ne pouvais pas le faire fonctionner avec HttpsUrlConnection. Mais le client HTTP d'Apache gère très bien les connexions persistantes avec des connexions SSL.

5
Biggie

J'ai rencontré exactement le même problème et j'ai finalement trouvé une solution après un débogage en profondeur.

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

Ceux-ci 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 renvoie -1 et le ferme également.
  • Les paramètres du socket sous-jacent doivent utiliser exactement les mêmes objets.
  • Vous devez appeler la déconnexion (oui, cela est contre-intuitif) sur le (s) URLConnection (s) lorsque vous avez terminé.

Dans le code ci-dessus, le problème est le suivant:

conn.setSSLSocketFactory(mySsl.getSocketFactory());

Enregistrer le résultat de getSocketFactory () dans une variable statique lors de l’initialisation, puis le transmettre à conn.setSSLSocketFactory devrait permettre de réutiliser le socket.

17
Bill Healey

L'établissement de la connexion SSL est très coûteux, que ce soit pour les appels de service ou pour obtenir de nombreuses ressources d'un navigateur.

Java Http(s)UrlConnection gère HTTP (S) Keep-Alive par défaut .

Je n'ai pas trouvé le code source du default SSLSocketFactory et probablement le mécanisme de maintien en vie y est implémenté. Pour confirmation, désactivez votre propre implémentation SSLSocketFactory pour un test, avec un magasin de clés de confiance personnalisé dans javax.net.ssl.trustStore afin que votre certificat auto-signé soit accepté.

Selon OpenJDK 7 ServerImpl implementation qui utilise ServerConfig la HttpsServer que vous avez utilisée émet un maintien en activité avec un délai d'expiration de 5 minutes par défaut.

Je vous propose de définir la propriété Sun.net.httpserver.debug sur true côté serveur pour obtenir des détails.

Attention, votre code n’ajoute pas l’en-tête Connection: close qui désactive le mécanisme de maintien en vie.

2
Yves Martin

Nous pouvons installer un serveur Web Apache, ajouter les directives suivantes pour voir si le fichier access.log d’Apache dispose d’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 demande de maintien après le premier, «2» le second, etc ...; sinon, il est toujours égal à 0 (indiquant la demande initiale).

0
WENPIN

essayez d'ajouter le code suivant:

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

Autant que je puisse comprendre HTTP/1.1 et HTTPS protocole, également documenté ici , Keep-Alive n'est pas un en-tête de bout en bout _ mais un hop-to-hop en-tête. Étant donné que SSL implique plusieurs étapes de négociation entre "différents sauts" (par exemple, CA et le serveur) pour chaque nouvelle connexion, je pense que Keep-Alive peut ne pas être applicable dans un contexte SSL. Ainsi, cela peut expliquer pourquoi l’en-têteKeep-Alive est ignoré à l’aide des connexions HTTPS. Sur la base de cette cette question , vous devrez peut-être vous assurer que un instance de connexion HTTP est utilisée pour garantir l'observation de Keep-Alive. En outre, dans la question, il semble qu'Apache HTTPClient ait été une meilleure solution.

0
nobeh

J'ai rencontré le même problème, et Bill Healey a raison . J'ai testé mon exemple de code ci-dessous avec quelques bibliothèques https . 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 cela vous aidera.

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

Mettre à jour:

Le partage de SSLSocketFactory permet de garder en vie. Le partage de SSLContext et l’obtention de facotry à chaque demande permettent la reprise de session. Je ne sais pas comment fonctionne la pile TLS, mais je viens de confirmer ces comportements de connexion avec certains appareils mobiles. 

Si vous souhaitez activer la persistance entre plusieurs classes, vous devez partager l'instance de SSLSocketFactory à l'aide d'un modèle singleton. 

Si vous souhaitez activer la reprise de session, assurez-vous que les paramètres de délai de session sont suffisamment longs côté serveur, tels que SSLSessionCacheTimeout (Apache), ssl_session_timeout (nginx).

0
forte916