Getting UnknownLengthHttpInputStream while getting InputStream from HttpURLConnection in android
Asked Answered
B

6

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.

Baseboard answered 8/8, 2012 at 5:32 Comment(13)
Attach the exception from logcat?Lockman
@Lockman I have attached the exception from logcat..Baseboard
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)?Lockman
Try printing 'xml' variable immediately after assigning value to it. Does it prints response on console or generates error?Killifish
@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.Baseboard
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");Lockman
For SOAP calls try to use KSoap2 instead HTTP callsKillifish
It seems this is a bug in android. Check this question. #4842425Sforza
Another one for your checklist - is the server producing valid XML? Probably try the url in a browser.Vaientina
@Baseboard Whats the response code do you got? I have the similar code working fine on my appJuli
@Baseboard I think its better if you post a detailed code snippet ...Juli
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 ?Ahl
are you sure that you need to do this ? connection.setRequestProperty("Accept", "application/xml");Suburbanite
R
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)

Ratal answered 2/12, 2012 at 19:36 Comment(0)
C
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/

Colincolinson answered 12/10, 2012 at 9:32 Comment(0)
T
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();
   }
Tameka answered 25/10, 2012 at 12:25 Comment(0)
C
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.

Cleft answered 27/11, 2012 at 17:40 Comment(0)
R
0

Once try it like this

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

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

//problem in URI/connection .....
}
Rellia answered 1/12, 2012 at 15:37 Comment(0)
S
-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()
Suburbanite answered 18/8, 2012 at 3:57 Comment(3)
The HTTP Protocol says that you can have a response without a Content-Length header in particular cases : 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.Ahl
What do you suggest then for a solution ?Suburbanite
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.Suburbanite

© 2022 - 2024 — McMap. All rights reserved.