2010-06-14 12 views
7

Uso HttpURLConnection per eseguire il POST HTTP ma non ottengo sempre la risposta completa. Volevo eseguire il debug del problema, ma quando passo a ogni riga ha funzionato. Ho pensato che dovesse essere un problema di temporizzazione, quindi ho aggiunto Thread.sleep e il mio codice ha funzionato davvero, ma questa è solo una soluzione temporanea. Mi chiedo perché sta succedendo e come risolverlo. Qui è il mio codice:HttpURLConnection non legge l'intera risposta

public static InputStream doPOST(String input, String inputMimeType, String url, Map<String, String> httpHeaders, String expectedMimeType) throws MalformedURLException, IOException { 

    URL u = new URL(url); 
    URLConnection c = u.openConnection(); 
    InputStream in = null; 
    String mediaType = null; 
    if (c instanceof HttpURLConnection) { 

     //c.setConnectTimeout(1000000); 
     //c.setReadTimeout(1000000); 

     HttpURLConnection h = (HttpURLConnection)c; 
     h.setRequestMethod("POST"); 
     //h.setChunkedStreamingMode(-1); 
     setAccept(h, expectedMimeType); 
     h.setRequestProperty("Content-Type", inputMimeType); 

     for(String key: httpHeaders.keySet()) { 
      h.setRequestProperty(key, httpHeaders.get(key)); 

      if (logger.isDebugEnabled()) { 
       logger.debug("Request property key : " + key + "/value : " + httpHeaders.get(key)); 
      } 

     } 

     h.setDoOutput(true); 
     h.connect(); 

     OutputStream out = h.getOutputStream(); 

     out.write(input.getBytes()); 

     out.close(); 

     mediaType = h.getContentType(); 

     logger.debug(" ------------------ sleep ------------------ START"); 
     try { 
      Thread.sleep(2000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     logger.debug(" ------------------ sleep ------------------ END"); 

     if (h.getResponseCode() < 400) { 
      in = h.getInputStream(); 
     } else { 
      in = h.getErrorStream(); 
     } 
    } 
    return in; 

} 

dopo ho effettuare le seguenti operazioni per leggere il flusso di input

 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     while (is.available() > 0) { 
      bos.write(is.read()); 
     } 
     is.close(); 

     //is.read(bytes); 
     if (logger.isDebugEnabled()) { 
      logger.debug(" Response lenght is : " + is.available()); 
      //logger.debug("RAW response is " + new String(bytes)); 
      logger.debug("RAW response is " + new String(bos.toByteArray())); 
     } 

E genearates le seguenti intestazioni HTTP

POST /emailauthentication/ HTTP/1.1 
Accept: application/xml 
Content-Type: application/xml 
Authorization: OAuth oauth_consumer_key="b465472b-d872-42b9-030e-4e74b9b60e39",oauth_nonce="YnDb5eepuLm%2Fbs",oauth_signature="dbN%2FWeWs2G00mk%2BX6uIi3thJxlM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1276524919", oauth_token="", oauth_version="1.0" 
User-Agent: Java/1.6.0_20 
Host: test:6580 
Connection: keep-alive 
Content-Length: 1107 

In altri post è stato suggerito di girare disattivare keep-alive utilizzando

http.keepAlive=false 

proprietà di sistema, ho provato e le intestazioni cambiato per

POST /emailauthentication/ HTTP/1.1 
Accept: application/xml 
Content-Type: application/xml 
Authorization: OAuth oauth_consumer_key="b465472b-d872-42b9-030e-4e74b9b60e39", oauth_nonce="Eaiezrj6X4Ttt0", oauth_signature="ND9fAdZMqbYPR2j%2FXUCZmI90rSI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1276526608", oauth_token="", oauth_version="1.0" 
User-Agent: Java/1.6.0_20 
Host: test:6580 
Connection: close 
Content-Length: 1107 

l'intestazione Connection è "vicino" ma ancora non in grado di leggere l'intera risposta. Qualche idea su cosa faccio di sbagliato?

+1

C'è qualche ambiguità nella tua domanda. * Di quale * risposta stai parlando? Con quell'intero codice in realtà non stai leggendo una risposta, ma creando una richiesta. Stai leggendo la sua risposta usando 'h.getInputStream()' in fondo, ma in realtà stai ignorando e/o non mostrando come lo elabori. – BalusC

+0

Hi BalusC Ho aggiunto le parti mancanti :) –

risposta

14

Credo che il problema è in questa linea:

while (is.available() > 0) { 

Secondo il javadoc, available non blocca e attendere che tutti i dati sono disponibili, così si potrebbe ottenere il primo pacchetto e poi tornerà falso . Il modo corretto di leggere da un InputStream è come questo:

int len; 
byte[] buffer = new byte[4096]; 
while (-1 != (len = in.read(buffer))) { 
    bos.write(buffer, 0, len); 
} 

Leggi tornerà -1 quando non c'è niente lasciato in InputStream o la connessione è chiusa, e si bloccherà e attendere che la rete, mentre farlo. La lettura degli array è anche molto più performante rispetto all'utilizzo di singoli byte.

+0

Ciao Jörn Questo era il problema ero sicuro che qualcosa è usabile sul livello di trasporto HTTP quindi leggere l'intero JavaDoc di HttpURLConnection, e non stavo prestando attenzione a InputStream Grazie! Peter –

+0

oh e ho dimenticato di menzionare il bug non è solo nel mio codice, ma anche Sun's JAXB. Inizialmente ho passato direttamente a InputStream a \t Object o = unmarshaller.unmarshal (bis); dove unmarshaller è instanceof javax.xml.bind.Unmarshaller e ha provocato lo stesso problema. –

0

Forse mi sono perso, ma qual è il tipo di "input" nel codice? Qualcosa di strano in InputStreams in generale è che i metodi di lettura (...) tendono a bloccarsi finché i dati non sono disponibili, quindi restituiscono solo quei dati. Dovrai effettivamente continuare a leggere da InputStream e accodare a un ByteArrayInputStream oa qualche altra struttura finché non forzerai esplicitamente una EOFException.

+1

È probabile un 'String' che rappresenta la querystring. Vedi anche [Come usare URLConnection] (http://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests). – BalusC

+0

Ciao Curtis Ho modificato il mio post per includere la firma del metodo che spiega quale input è e lo snipplet di codice che legge InputStream –

+0

'finché non forzate esplicitamente una EOFException'. Ma non ne otterrai uno se chiami read() o readLine(). Si ottiene EOFException solo quando si chiama readXXX() per qualsiasi altra X. I metodi read() restituiscono -1 a EOS.Il codice di esempio nella risposta di Jörn Horstmann è la tecnica corretta. – EJP

0

Se si sta leggendo l'intero messaggio in una volta è possibile confrontare l'isr.available() alla lunghezza del contenuto previsto. Ecco come l'ho fatto:

public byte[] readData(HttpURLConnection conn) 
     throws IOException, InterruptedException { 
    String _connlen = conn.getHeaderField("Content-Length"); 
    int connlen = Integer.parseInt(_connlen); 
    InputStream isr = null; 
    byte[] bytes = new byte[connlen]; 

    try { 
     isr = conn.getInputStream(); 

     //security count that it doesn't begin to hang 
     int maxcounter = 0; 
     //wait till all data is avalibal, max 5sec 
     while((isr.available() != connlen) && (maxcounter < 5000)){ 
      Thread.sleep(1); 
      maxcounter++; 
     } 
     //Throw if not all data could be read 
     if(maxcounter >= 5000) 
      throw new IllegalAccessError(); 

     //read the data   
     if(isr.read(bytes, 0, connlen) < 0) 
      throw new IllegalAccessError();  


    } finally { 
     if (isr != null) 
      isr.close(); 
    } 

    return bytes; 
}