2010-06-14 21 views
7

Uso HttpURLConnection para hacer HTTP POST, pero no siempre obtengo la respuesta completa. Quería depurar el problema, pero cuando paso por cada línea, funcionó. Pensé que debía ser un problema de tiempo, así que agregué Thread.sleep y realmente hizo que mi código funcionara, pero esto es solo una solución temporal. Me pregunto por qué está sucediendo esto y cómo resolverlo. Aquí está mi código:HttpURLConnection no lee toda la respuesta

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; 

} 

después, hacer lo siguiente para leer el flujo de entrada

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

Se genearates las siguientes cabeceras 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 

En otros mensajes se sugirió que se encienda apagado keep-alive usando el

http.keepAlive=false 

propiedad del sistema, lo he intentado y las cabeceras cambiado a

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 

la cabecera de conexión está "cerca" pero todavía no puede leer toda la respuesta. Alguna idea de que hago mal?

+1

Hay una ambigüedad en su pregunta. * ¿De qué * respuesta estás hablando? Con todo ese código, en realidad no estás leyendo una respuesta, sino creando una solicitud. Estás leyendo su respuesta usando 'h.getInputStream()' en la parte inferior, pero en realidad lo estás ignorando y/o no muestra cómo lo procesas. – BalusC

+0

Hola BalusC Agregué las piezas faltantes :) –

Respuesta

14

Creo que su problema es en esta línea:

while (is.available() > 0) { 

Según el javadoc, available no bloquea y esperar hasta que todos los datos están disponibles, por lo que podría conseguir el primer paquete y luego volverá falsa . La forma correcta de leer desde un InputStream es así:

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

Leer devolverá -1 cuando no queda nada en el flujo de entrada o la conexión se cierra, y se bloqueará y esperar a que la red mientras lo hace. Leer arreglos también es mucho más eficiente que usar bytes individuales.

+0

Hola Jörn Este es el problema que estaba seguro de que algo está pasando mal en el nivel de transporte HTTP, así que lea todo el JavaDoc de HttpURLConnection, y no prestó atención a InputStream ¡Gracias! Peter –

+0

ah y olvidé mencionar que el error no está solo en mi código, sino también en JAXB de Sun. Originalmente pasé el InputStream directamente al \t Objeto o = unmarshaller.unmarshal (bis); donde unmarshaller es instanciaof javax.xml.bind.Unmarshaller y resultó el mismo problema. –

0

Quizás me lo perdí, pero ¿cuál es el tipo de datos de "entrada" en tu código? Algo extraño en InputStreams en general es que los métodos de lectura (...) tienden a bloquearse hasta que los datos estén disponibles, y luego devuelven solo esos datos. De hecho, tendrá que seguir leyendo desde InputStream y anexar a ByteArrayInputStream u otra estructura hasta que fuerce explícitamente una EOFException.

+1

Es probable que sea una 'Cadena' que representa la cadena de consulta. También vea [Cómo usar URLConnection] (http://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests). – BalusC

+0

Hola Curtis Edité mi publicación para incluir la firma del método que explica qué entrada es y el código snipplet que lee InputStream –

+0

'hasta que explícitamente se fuerza una EOFException'. Pero no obtendrá uno si está llamando a read() o readLine(). Solo obtiene EOFException cuando llama a readXXX() para cualquier otra X. Los métodos read() devuelven -1 en EOS.La muestra del código en la respuesta de Jörn Horstmann es la técnica correcta. – EJP

0

Si está leyendo el mensaje completo de una vez, puede comparar isr.available() con la duración esperada del contenido. Así es como lo hice:

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; 
} 
Cuestiones relacionadas