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.
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.
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:
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.
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.
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).
essayez d'ajouter le code suivant:
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");
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.
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();
}
}
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).