web-dev-qa-db-fra.com

Utilisation sûre de HttpURLConnection

Lors de l'utilisation de HttpURLConnection, InputStream doit-il être fermé si nous ne le récupérons pas et ne l'utilisons pas?

c'est-à-dire est-ce sûr?

HttpURLConnection conn = (HttpURLConnection) uri.getURI().toURL().openConnection();
conn.connect();
// check for content type I don't care about
if (conn.getContentType.equals("image/gif") return; 
// get stream and read from it
InputStream is = conn.getInputStream();
try {
    // read from is
} finally {
    is.close();
}

Deuxièmement, est-il sûr de fermer un InputStream avant que tout son contenu ne soit entièrement lu ?

Existe-t-il un risque de laisser le socket sous-jacent dans l'état ESTABLISHED ou même CLOSE_WAIT?

47
Joel

est-il sûr de fermer un InputStream avant que tout son contenu ait été lu

Vous devez lire toutes les données dans le flux d'entrée avant de le fermer afin que la connexion sous-jacente TCP soit mise en cache. J'ai lu que cela ne devrait pas être requis dans le dernier Java, mais c'était toujours mandaté pour lire la réponse entière pour la réutilisation de connexion.

Vérifiez cet article: garder en vie dans Java6

28
Cratylus

Selon http://docs.Oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html et le code source d'OpenJDK.

(Lorsque keepAlive == true)

Si le client a appelé HttpURLConnection.getInputSteam().close(), l'appel ultérieur à HttpURLConnection.disconnect() ne fermera pas le Socket . c'est-à-dire que le Socket est réutilisé (mis en cache)

Si le client n'appelle pas close(), appelez disconnect() fermera le InputStream et fermera le Socket.

Donc, pour réutiliser le Socket, il suffit d'appeler InputStream.close(). N'appelez pas HttpURLConnection.disconnect().

34
Anderson Mao

Voici quelques informations concernant le cache keep-alive. Toutes ces informations concernent Java 6, mais sont probablement également exactes pour de nombreuses versions antérieures et ultérieures.

D'après ce que je peux dire, le code se résume à:

  1. Si le serveur distant envoie un en-tête "Keep-Alive" avec une valeur de "timeout" qui peut être analysée comme un entier positif, ce nombre de secondes est utilisé pour le timeout.
  2. Si le serveur distant envoie un en-tête "Keep-Alive" mais qu'il n'a pas de valeur "timeout" qui peut être analysée comme un entier positif et "usingProxy" est vrai, puis le délai d'attente est de 60 secondes.
  3. Dans tous les autres cas, le délai est de 5 secondes.

Cette logique est divisée en deux endroits: autour de la ligne 725 de Sun.net.www.http.HttpClient (dans la méthode "parseHTTPHeader"), et autour de la ligne 120 de Sun.net. www.http.KeepAliveCache (dans la méthode "put").


Il existe donc deux façons de contrôler le délai d'expiration:

  1. Contrôlez le serveur distant et configurez-le pour envoyer un en-tête Keep-Alive avec le champ de temporisation approprié
  2. Modifiez le code source JDK et créez le vôtre.

On pourrait penser qu'il serait possible de modifier la valeur par défaut de cinq secondes apparemment arbitraire sans recompiler les classes JDK internes, mais ce n'est pas le cas. Un bug a été déposé en 2005 demandant cette capacité, mais Sun a refusé de la fournir.

18
Samuel Edwin Ward

Si vous voulez vraiment vous assurer que la connexion est fermée, vous devez appeler conn.disconnect().

Les connexions ouvertes que vous avez observées sont dues à la fonctionnalité de maintien de la connexion HTTP 1.1 (également connue sous le nom de Connexions persistantes HTTP ). Si le serveur prend en charge HTTP 1.1 et n'envoie pas de Connection: close dans l'en-tête de réponse Java ne ferme pas immédiatement la connexion sous-jacente TCP lorsque vous fermez le flux d'entrée. Au lieu de cela, il le maintient ouvert et essaie de le réutiliser) pour la prochaine requête HTTP au même serveur.

Si vous ne voulez pas du tout ce comportement, vous pouvez définir la propriété système http.keepAlive à faux:

System.setProperty("http.keepAlive","false");
7
Robert

Vous devez également fermer le flux d'erreurs si la demande HTTP échoue (tout sauf 200):

try {
  ...
}
catch (IOException e) {
  connection.getErrorStream().close();
}

Si vous ne le faites pas, toutes les requêtes qui ne renvoient pas 200 (par exemple timeout) feront fuir un socket.

3
Vlad Lifliand

Lors de l'utilisation de HttpURLConnection, InputStream doit-il être fermé si nous ne le récupérons pas et ne l'utilisons pas?

Oui, il doit toujours être fermé.

c'est-à-dire est-ce sûr?

Pas à 100%, vous courez le risque d'obtenir un NPE. Plus sûr est:

InputStream is = null;
try {
    is = conn.getInputStream()
    // read from is
} finally {
    if (is != null) {
        is.close();
    }
}
1
Arjan Tijms