1

I'm trying to do a HTTP GET using the HttpURLConnection object in Android.

UPDATE

I tried connection to a different server. This is also hosted within Cloud 9 (c9.io) and also returns a json response. This time I'm not getting a 301 redirect, but I am getting the actual response the server is supposed to send.

Since this means the problem is localised within the server, I've reorganized the following sections in order to focus reading onto the server-related information. Android related information has been moved to the end of the question.

Where I am connecting:

  • Development server on Cloud9
  • Using the Laravel Framework 5.2 (we cannot upgrade to 5.3 at this time, due to unsupported project dependencies)
  • The server should return a JSON answer
  • If I connect to the same URL through the browser I get the correct response (JSON string. Required HTTP Headers and a '200' HTTP Response Code)

Where I am connecting FROM

  • Android phone (Oneplus 3, on Android 6.0)
  • Compile SDK version: 23
  • Using Build Tools: "23.0.3"
  • Using Min SDK verion: 19
  • Using Target SDK version: 22
  • I'm connectiong using a HttpURLConnection object, using HTTP Method 'GET'

HTTP Response on Android

When I run my code I get the folling result from the server:

The HTTP response code is 301 but the message is null.

  1. The new URL is exactly the same, but using HTTPS. It seems server is somehow forcing SSL/TSL encryption. Which does not happen when accessing HTTP from the browser.

HTTP Header (on Android):

  • date => Tue, 04 Oct 2016 05:56:26 GMT
  • location => https://domain.com/route/ (I modified this bit)
  • content-length => 382
  • content-type => text/html; charset=iso-8859-1
  • X-BACKEND => apps-proxy
  • X-Android-Selected-Protocol => http/1.1
  • X-Android-Sent-Millis => 1475560583894
  • X-Android-Received-Millis => 1475560585637
  • X-Android-Response-Source => NETWORK 301
  • null => HTTP/1.1 301

Other data

  1. Since it seems the server wants Android to use HTTPS, I tried modifying the code to use HTTPS (HttpsURLConnection). This may or may not solve this problem, but I am unable to check it since I get an annoying SSL handshake failed error. Plus I have no need for encryption on this application, and therefore I'm reluctant to solve the problems coming with it.
  2. This is all running within an AsyncTask object (since Android get moody when you try to use a network connection on the main thread).
  3. Setting up a new server (outside of Cloud 9 and without any SSL/TSL) could be an option, but I'm reluctant to do this since it would be quite time consuming.
  4. I tried connecting to another Cloud 9 server (which also returns a json response), using the exact same code, and everything works correctly. This suggests that the problem arises from the HTPP 301 error.

I will try to share with you any other information you may require to answer my question!

Native Android stuff (moved on UPDATE, see above)

The response content seems to be an incomplete JSON:

{ 'status':'ERROR'

Note I did NOT forget the closing } character, that's what the response actually containts. This is injected somewhere unknown (to me) during the workflow. When I capture the HTTP response (using Charles on my PC, which is set as a Proxy for my phone's Wi-Fi connection) it's content is (as expected) a simple HTML telling you to redirect (HTPP code 301) to a new route.

The invalid JSON code (above) isn't there, but a valid HTML is.

This would suggest that the invalid JSON appears somewhere internally to my code (not on the server, or transport). But there is no code on my app that generates a JSON string, let alone inject it into the response I'm processing.

Code for the HttpURLConnection

this.setURL(ruta); //gets correct url
HttpURLConnection cxn = (HttpURLConnection) this.getURL().openConnection(); //init
cxn.setRequestMethod("GET"); //use HTTP GET verb
cxn.setUseCaches(false); //no cache
cxn.setRequestProperty("Cache-Control", "no-cache"); //even less cache
cxn.setDoOutput(false); //only true in POST/PUT requests
cxn.setRequestProperty("Connection","keep-alive");
cxn.setRequestProperty("DNT", "1"); //TEMP
cxn.setInstanceFollowRedirects(true); //should follow redirects
cxn.setRequestProperty( "charset", "utf-8");

Code for the reading the result

int status_code = cxn.getResponseCode();
InputStream responseStream = new BufferedInputStream(cxn.getInputStream());
BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(responseStream));
String line = "";
StringBuilder stringBuilder = new StringBuilder();
while ((line = responseStreamReader.readLine()) != null) {
    stringBuilder.append(line).append("\n");
}
responseStreamReader.close();
String response = stringBuilder.toString();
cxn.disconnect();
Pablo K
  • 410
  • 4
  • 15
  • 2
    `"Content-Type", "application/x-www-form-urlencoded");` ???? For a GET request? I suggest to drop all other request properties too. – greenapps Oct 04 '16 at 07:27
  • Having problems receicing the right data i expected you to post the code who reads it. But you did not. – greenapps Oct 04 '16 at 07:28
  • Just added the code for that, end of the question. Still (as I explained on the previous update) I can receive the correct data from other servers. @greenapps – Pablo K Oct 04 '16 at 07:33
  • @greenapps You are right about the content-type property. I had added it because it appeared on the header the browser sent, but I checked again and it's not there (I might have read wrong). I removed it. Sadly, that did not fix anything – Pablo K Oct 04 '16 at 07:36
  • 1
    GET and `doOutput(false)` and `Connection: keep-alive` are the defaults. Don't write pointless code. – user207421 Oct 04 '16 at 07:52
  • @EJP Removed it. Still, removing redundant code won't fix my problem. – Pablo K Oct 04 '16 at 08:13

4 Answers4

2

Remove the code you've used to create the HttpURLConnection and try with this one:

URL url;
HttpURLConnection urlConnection = null;
try {
    url = new URL("http://www.domain.com/index.aspx?parameter1=X&parameter2=X"); //Use your url and add the GET parameters

    urlConnection = (HttpURLConnection) url.openConnection();

    urlConnection.setInstanceFollowRedirects(false); /* added line */

    InputStream in = urlConnection.getInputStream();

    InputStreamReader isw = new InputStreamReader(in);

    int data = isw.read();
    while (data != -1) {
        char current = (char) data;
        data = isw.read();
        System.out.print(current);
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (urlConnection != null) {
        urlConnection.disconnect();
    }    
}

This should be all you need to set for your GET request.

EDIT:

I've tested the webservice using Volley, here's the code I've used in order to retrieve the webservice response:

public class MainActivity extends AppCompatActivity {

  public String response;
  TextView textView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView = (TextView) findViewById(R.id.rTextView);

    RequestQueue queue = Volley.newRequestQueue(this);
    String url = "yourWebserviceUrl";

    // Request a string response from the provided URL. 
    StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
      new Response.Listener < String > () {
        @Override
        public void onResponse(String response) {
          textView.setText("Response is: " + response);
        }
      }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
          textView.setText("That didn't work!");
        }
      });
    // Add the request to the RequestQueue. 
    queue.add(stringRequest);

  }

}

And this is the response I got:

{"status":"ok","found":false,"extra":"App\\Scanners"}
LS_
  • 6,763
  • 9
  • 52
  • 88
  • I have shut down my computer for today. I'll try this in a few hours though! (deadline is getting closer and closer) – Pablo K Oct 04 '16 at 08:00
  • @PabloKvitca hope it works! :) Let me know if you find any problems with the code! – LS_ Oct 04 '16 at 08:03
  • I tried this, the code works correctly. But the problem persists. I'm stil getting an unexplained HTTP 301 response from the server – Pablo K Oct 04 '16 at 14:09
  • @PabloKvitca It's really weird, can you try the edited code I've posted above? I've just added the `setInstanceFollowRedirects` to `false` before opening the connection – LS_ Oct 04 '16 at 14:19
  • I will try it. But won't the url.openConnection() just overwrite anything I've set before? I believe this should go after that bit. i'm trying it as you wrote it anyway – Pablo K Oct 04 '16 at 14:22
  • Okay I tried that. Got a NullPointerException (connection is null before url.openConnection()). Moved it after that though. Still same response – Pablo K Oct 04 '16 at 14:27
  • @PabloKvitca yeah I'm sorry, my bad! Btw if it's possible can you share the url to the webservice? – LS_ Oct 04 '16 at 14:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/124905/discussion-between-pablo-kvitca-and-signo). – Pablo K Oct 04 '16 at 14:41
  • so everything is working now. Thanks! Just FYI: I tried to encapsulate the server behaviour into a class. And when I tried it again. I once more got a 301. It seems that if the Volley queue isn't on an Activity or isn't global, it cannot handle redirects. (probably the same with HttpURLConnection) Anyway this solution will work with a few adjustments! Thanks again! – Pablo K Oct 06 '16 at 02:54
  • @PabloKvitca No problem and i'm glad it worked! Regarding the redirect, you could try to catch the redirect and implement your own logic with Volley, take a look at this [answer](http://stackoverflow.com/questions/17481964/android-volley-to-handle-redirect#answer-28454312) – LS_ Oct 06 '16 at 12:05
  • I don't think that's necessary. I managed to get the RequestQueue to handle the redirects successfully using the AppController class from this tutorial http://www.androidhive.info/2014/05/android-working-with-volley-library-1/ It makes sure there is only one instance of the Volley queue and that it accesible globally. Everything seems to work as expected now! – Pablo K Oct 07 '16 at 00:46
2

Changing the protocol to https worked for me.

Jozsef Lehocz
  • 330
  • 3
  • 21
  • That solution only works if you have a secure https server, which at the moment I didn't. Still, it is valid. – Pablo K Jun 10 '18 at 23:25
2

I faced the same problem, and I fixed it after reading this source.
All we need to do is handling 3** errors like shown below

if(responseCode > 300 && responseCode < 400) {

    String redirectHeader = conn.getHeaderField("Location");
    if(TextUtils.isEmpty(redirectHeader)) {                       
        return new JsonResponse(responseCode, "Failed to redirect");
    }
    JsonRequest newRequest = request;
    newRequest.url = redirectHeader;
    return getJsonFromUrl(newRequest);
}

Each 3** response should have a header with name Location which contains a redirect link which we should use.

Kostya M
  • 205
  • 1
  • 9
0

Change the line : HttpURLConnection cxn = (HttpURLConnection) this.getURL().openConnection();

with : HttpsURLConnection cxn = (HttpsURLConnection) this.getURL().openConnection();

So you will able to handle https

Harry Cotte
  • 90
  • 2
  • 14