16

HttpURLConnection.getInputStream() gives UnknownLengthHttpInputStream and due to this Document parsing throws SAX parser exception.

Following is the code

try{
    URL url = new URL(uri);
    HttpURLConnection connection =
    (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    connection.setRequestProperty("Accept", "application/xml");

    InputStream xml = connection.getInputStream();
    System.out.println(connection.getResponseCode());
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(connection.getInputStream());
    doc.getDocumentElement().normalize();

}catch(Exception e){
    e.printStackTrace();
}

Any one knows the reason for UnknownLengthHttpInputStream. I'm getting this error only in android and this code works perfectly in Java Project.

Following is the exception from LogCat:

08-08 11:07:40.490: W/System.err(1493): org.xml.sax.SAXParseException: Unexpected end of document
08-08 11:07:40.504: W/System.err(1493): at org.apache.harmony.xml.parsers.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:129)
08-08 11:07:40.510: W/System.err(1493): at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:107)
08-08 11:07:40.510: W/System.err(1493): at com.example.testws.MainActivity.onCreate(MainActivity.java:59)
08-08 11:07:40.520: W/System.err(1493): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
08-08 11:07:40.520: W/System.err(1493): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
08-08 11:07:40.520: W/System.err(1493): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
08-08 11:07:40.520: W/System.err(1493): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
08-08 11:07:40.530: W/System.err(1493): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)

Thanks in advance.

Visruth
  • 3,430
  • 35
  • 48
Achsah
  • 543
  • 4
  • 16
  • 1
    Attach the exception from logcat? – Jens Aug 08 '12 at 07:18
  • @Jens I have attached the exception from logcat.. – Achsah Aug 08 '12 at 10:37
  • 3
    you get the `UnknownLengthHttpInputStream` stream because your server isn't supplying a `Content-Length` or using chunked transfer encoding. When the server does not supply either it must signal the end of the HTTP body by closing the connection - which it appears to do mid-document in this case. Do you have any possibility to sniff the web server traffic when performing this call (e.g. using *WireShark*)? – Jens Aug 08 '12 at 10:42
  • 1
    Try printing 'xml' variable immediately after assigning value to it. Does it prints response on console or generates error? – Chanchal Shelar Aug 08 '12 at 11:19
  • @Chanchal the xml variable prints the following com.example.testws.DoneHandlerInputStream@b6c34930 - But as mentioned above db.parse(xml) throws org.xml.sax.SAXParseException: Unexpected end of document. When I inspect the XML element it shows UnknownLengthHttpInputStream. – Achsah Aug 09 '12 at 05:16
  • `UnknownLengthHttpInputStream` is just a subclass of `AbstractHttpInputStream` etc. etc. - i.e. it's just as much a stream as any other. The issue here is that your server isn't sending a `Content-Length` or a valid transfer encoding - which is why libcore is giving you this specific impl. of `InputStream`. If you want to print the contents of the stream you should read it yourself, e.g. like this: `ByteArrayOutputStream o = new ByteArrayOutputStream(); int c; byte[] b = new byte[1024]; while ((c = xml.read(b))!=-1){o.write(b,0,c);} System.out.println(b.toString("UTF-8");` – Jens Aug 09 '12 at 07:08
  • 1
    For SOAP calls try to use KSoap2 instead HTTP calls – Chanchal Shelar Aug 10 '12 at 10:02
  • It seems this is a bug in android. Check this question. http://stackoverflow.com/questions/4841925/java-io-ioexception-bufferedinputstream-is-closed-in-android-2-3 – Serdar Samancıoğlu Aug 10 '12 at 10:22
  • Another one for your checklist - is the server producing valid XML? Probably try the url in a browser. – sgp15 Aug 16 '12 at 14:33
  • @Achsah Whats the response code do you got? I have the similar code working fine on my app – Renjith K N Aug 16 '12 at 17:28
  • @Achsah I think its better if you post a detailed code snippet ... – Renjith K N Aug 16 '12 at 17:38
  • It's quite odd that your web server is serving an XML file without Content-Length http header. Has it been configured for that ? Is it a public resource (URL ?) that we could test ? – Jérôme Radix Aug 17 '12 at 11:46
  • are you sure that you need to do this ? connection.setRequestProperty("Accept", "application/xml"); – Siddharth Tyagi Jan 30 '13 at 16:16

6 Answers6

3

Its probably a Http 1.0 (old server or misconfiguration) server or no keep alive configured. In this case the length of the stream is known at the time the connection is closed from the server. Try specifying http1.1 and keep-alive in your request header (some googling will help on that). Only if the content length attribute is specified in the server response, you'll know the stream length in advance.

Workaround: read the http stream into a ByteBufferStream completely (until read() returns -1). Then throw the ByteBufferInputStream to your library (length is known now)

Matteo
  • 14,696
  • 9
  • 68
  • 106
R.Moeller
  • 3,436
  • 1
  • 17
  • 12
0

Have you tried to use the apache libraries for this? I would suggest the following:

try {
        HttpClient client = new DefaultHttpClient();  
        String getURL = "http://www.google.com";
        HttpGet get = new HttpGet(getURL);
        HttpResponse responseGet = client.execute(get);  
        HttpEntity resEntityGet = responseGet.getEntity();  
        if (resEntityGet != null) {  
                    //do something with the response
                    Log.i("GET RESPONSE",EntityUtils.toString(resEntityGet));
                }
} catch (Exception e) {
    e.printStackTrace();
}

and then get the stream for the HttpEntity itself. Smth like:

InputStream st = entity.getContent();

More examples here: http://www.softwarepassion.com/android-series-get-post-and-multipart-post-requests/

Kris
  • 5,714
  • 2
  • 27
  • 47
0

To handle response use this method it will take care of all !!

public static ResponseHandler<String> getResponseHandlerInstance(final Handler handler) {
      final ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

         public String handleResponse(final HttpResponse response) {
            Message message = handler.obtainMessage();
            Bundle bundle = new Bundle();
            StatusLine status = response.getStatusLine();
            Log.d(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG + " statusCode - " + status.getStatusCode());
            Log.d(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG + " statusReasonPhrase - " + status.getReasonPhrase());
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
               try {
                  result = HTTPRequestHelper.inputStreamToString(entity.getContent());
                  bundle.putString("RESPONSE", result);
                  message.setData(bundle);
                  handler.sendMessage(message);
               } catch (IOException e) {
                  Log.e(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG, e);
                  bundle.putString("RESPONSE", "Error - " + e.getMessage());
                  message.setData(bundle);
                  handler.sendMessage(message);
               }
            } else {
               Log.w(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG + " empty response entity, HTTP error occurred");
               bundle.putString("RESPONSE", "Error - " + response.getStatusLine().getReasonPhrase());
               message.setData(bundle);
               handler.sendMessage(message);
            }
            return result;
         }
      };
      return responseHandler;
   }

   private static String inputStreamToString(final InputStream stream) throws IOException {
      BufferedReader br = new BufferedReader(new InputStreamReader(stream));
      StringBuilder sb = new StringBuilder();
      String line = null;
      while ((line = br.readLine()) != null) {
         sb.append(line + "\n");
      }
      br.close();
      return sb.toString();
   }
0

Here the problem comes when SAX parser could not get the length of InputStream data. To fix this problem, save the contents read from the xml (InputStream) in to a String variable and make an InputStream from the variable for parse method of DocumentBuilder. To do this modify your code as follows :

try {
            URL url = new URL(uri);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Accept", "application/xml");
            InputStream xml = connection.getInputStream();

            DataInputStream dis = new DataInputStream(xml);
            StringBuilder sb = new StringBuilder();
            int asci = dis.read();
            while (asci > 0) {
                sb.append((char) asci);
                asci = dis.read();
            }
            String inputXML = sb.toString();            
            String predefinedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <EmotionDB></EmotionDB>";
            InputStream inputStream = new ByteArrayInputStream(inputXML.getBytes());//use predefinedXML.getBytes() instead of inputXML.getBytes() for sample test


            System.out.println(connection.getResponseCode());
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(inputStream);
            doc.getDocumentElement().normalize();

        } catch (Exception e) {
            e.printStackTrace();
        }

Here the inputStream will have a correct content length as it is built from a ByteArrayInputStream with a fixed number of bytes.

Visruth
  • 3,430
  • 35
  • 48
0

Once try it like this

if(connection.getResponseCode() ==HttpURLConnection.HTTP_OK){

  InputStream xml = connection.getInputStream();   
  ..........
}else{

//problem in URI/connection .....
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
CR Sardar
  • 921
  • 2
  • 17
  • 32
-2

You will need to close the output stream properly within the service to avoid this exception. If you are using a third party library then make sure that you have set the response header

Content-Type
Content-Length

If you are using a java service you can get the content-length from method

File.length()
  • 2
    The HTTP Protocol says that you can have a response without a Content-Length header in particular cases : http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 Moreover, you can not always modify the service you're calling because it's not always yours. – Jérôme Radix Aug 25 '12 at 15:03
  • What do you suggest then for a solution ? – Siddharth Tyagi Sep 03 '12 at 08:17
  • honestly I think it is because of fixing the request type. by `connection.setRequestProperty("Accept", "application/xml");` You may not know how is the URL responding. – Siddharth Tyagi Jan 30 '13 at 16:22