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.
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.
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.
é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.
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 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.
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).
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).