2011-01-22 13 views
35

Quando si utilizza HttpURLConnection, l'InputStream deve essere chiuso se non viene "ricevuto" e utilizzato?Uso sicuro di HttpURLConnection

cioè è sicuro?

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

In secondo luogo, è sicuro di chiudere un InputStream prima di tutto il suo contenuto è stato completamente leggere?

C'è il rischio di lasciare il socket sottostante in stato ESTABLISHED o anche CLOSE_WAIT?

risposta

25

E 'sicuro per chiudere un InputStream prima di tutto il suo contenuto è stato leggere

Hai bisogno di leggere tutti i dati nel flusso di ingresso prima di chiudere in modo che la connessione TCP sottostante venga memorizzata nella cache. Ho letto che non dovrebbe essere richiesto nell'ultima versione di Java, ma è sempre stato richiesto di leggere l'intera risposta per il riutilizzo della connessione.

Controllare questo post: keep-alive in java6

+0

Molto interessante. Questa domanda è in realtà lo sfondo per un problema che ho dove vedo LOTTI di socket CLOSE_WAIT sullo stesso IP, ma a causa del caching (non richiamo esplicitamente URLConnection.disconnect(), mi aspetto che ci sia solo uno, che dovrebbe essere riutilizzato – Joel

+3

@Joel: chiamando 'HttpUrlConnection.disconnect()' il socket tcp sottostante viene chiuso. Chiudendo il flusso di input, il socket tcp sottostante viene riunito per un successivo riutilizzo.L'unica avvertenza è che l'intera risposta (OPPURE l'intera risposta all'errore) dovrebbe essere letta dal flusso di input affinché la connessione tcp sia memorizzata nella cache. Questo è sempre stato consigliato indipendentemente dal fatto che tu abbia effettivamente bisogno dell'intero dato dal flusso. Controllare la posta nella mia risposta – Cratylus

+0

Che cosa succede se non avete letto tutti i dati, come per il mio primo esempio - sto indovinare ancora necessario chiudere la IS, ma sarà non ancora essere memorizzato nella cache, se non i dati vengono letti ma è ancora chiuso. – Joel

1

Quando si utilizza HttpURLConnection, l'InputStream deve essere chiuso se non viene "ricevuto" e utilizzato?

Sì, deve sempre essere chiuso.

cioè è sicuro?

Non al 100%, si corre il rischio di ottenere un NPE. Più sicuro è:

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

La seconda domanda era in riferimento allo stato del socket sottostante, ho deliberatamente pubblicato uno snippet incompleto per quanto riguarda la sicurezza del codice runtime completo. Voglio davvero sapere se c'è un pericolo di un socket rimasto in CLOSE_WAIT o ESTABLISED chiudendo il socket prima che tutto il contenuto sia letto, – Joel

+0

o 'IOUtils.closeQuietly (is)' – Kirby

6

Se davvero si vuole fare in modo che la connessione è vicino si dovrebbe chiamare conn.disconnect().

Le connessioni aperte osservate sono a causa della funzionalità keep alive della connessione HTTP 1.1 (nota anche come HTTP Persistent Connections). Se il server supporta HTTP 1.1 e non invia un Connection: close nell'intestazione della risposta, Java non chiude immediatamente la connessione TCP sottostante quando si chiude il flusso di input. Invece lo tiene aperto e tenta di riutilizzarlo per la successiva richiesta HTTP allo stesso server.

Se non si desidera che questo comportamento a tutti è possibile impostare la proprietà di sistema http.keepAlive su false:

System.setProperty("http.keepAlive","false"); 
+1

Grazie. Supponendo che la connessione non sia in uso sai per quanto tempo viene memorizzata nella cache prima di essere chiusa, e c'è un modo per controllare questo periodo di timeout? – Joel

14

Ecco alcune informazioni riguardanti la cache keep-alive. Tutte queste informazioni riguardano Java 6, ma probabilmente sono anche accurate per molte versioni precedenti e successive.

Da quello che posso dire, il codice si riduce a:

  1. Se il server remoto invia un'intestazione "Keep-Alive" con un valore di "timeout" che può essere analizzato come un numero intero positivo, che il numero di secondi è usato per il timeout.
  2. Se il server remoto invia un'intestazione "Keep-Alive" ma non ha un valore di "timeout" che può essere analizzato come numero intero positivo e "usingProxy" è true, quindi il timeout è 60 secondi.
  3. In tutti gli altri casi, il timeout è 5 secondi.

Questa logica è diviso tra due posizioni: intorno alla linea 725 di sun.net.www.http.HttpClient (nel metodo "parseHTTPHeader"), e intorno alla linea 120 di sun.net.www.http.KeepAliveCache (nel metodo "put").


Quindi, ci sono due modi per controllare il periodo di timeout:

  1. controllo il server remoto e configurarlo per inviare un colpo di testa di keep-alive con il corretto campo Timeout
  2. Modificare il JDK codice sorgente e costruisci il tuo.

Si potrebbe pensare che sarebbe possibile modificare il predefinito apparentemente arbitrario di cinque secondi senza ricompilare le classi JDK interne, ma non lo è. A bug è stato archiviato nel 2005 richiedendo questa capacità, ma Sun si è rifiutato di fornirlo.

+3

Bella ricerca su un argomento scarsamente documentato. Grazie per aver condiviso. –

31

Secondo http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html e codice sorgente OpenJDK.

(Quando Keepalive == true)

Se il cliente chiama HttpURLConnection.getInputSteam(). close(), la successiva chiamata a HttpURLConnection. disconnessione() NON chiudi la presa. Il socket viene riutilizzato (nella cache)

Se il client non chiama close(), chiama disconnect() chiude InputSteam e chiude la presa.

Quindi, per riutilizzare il socket, è sufficiente chiamare InputStream close(). Non chiamare HttpURLConnection disconnettere().

2

Hai anche a chiudere flusso di errore se la richiesta HTTP non riesce (tutt'altro che 200):

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

Se non lo fai, tutte le richieste che non restituiscono 200 (ad esempio timeout) sarà perdita di una presa. Vedi http://scotte.github.io/2015/01/httpurlconnection-socket-leak/ per maggiori dettagli.

+1

Non proprio sicuro di ciò - l'ultimo codice sorgente (JDK 8u74) legge 'public InputStream getErrorStream() { return null; } ' – FelixJongleur42