0

I wrote some code that tries to communicate with a website "as a browser" (in terms of cookies & headers). I currently have four requests (GET, POST, POST, GET).

The code is pretty straightforward: opening a connection, adding headers and cookies, parsing response.

GET code:

conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", userAgent);
conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
if (cookies != null) {
    for (String cookie : this.cookies) {
        conn.addRequestProperty("Cookie", cookie.split(";", 1)[0]);
    }
}
int responseCode = conn.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();

setCookies(conn.getHeaderFields().get("Set-Cookie"));

return response.toString();

POST code:

conn = (HttpsURLConnection) url.openConnection();
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Host", "...");
conn.setRequestProperty("User-Agent", userAgent);
conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
conn.setRequestProperty("Accept-Encoding","identity");
for (String cookie : this.cookies) {
    conn.addRequestProperty("Cookie", cookie.split(";", 1)[0]);
}
conn.setRequestProperty("Connection", "keep-alive");
conn.setRequestProperty("Referer", "https://...");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", Integer.toString(postParams.length()));

conn.setDoOutput(true);
conn.setDoInput(true);

DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.writeBytes(postParams);
wr.flush();
wr.close();

int responseCode = conn.getResponseCode();
InputStream is = responseCode != 400 ? conn.getInputStream() : conn.getErrorStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String inputLine;
StringBuilder response = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();

return response.toString();

The program works well on my PC. However, when running it on an Android device, I face multiple problems on the second POST; on one device there's Too many redirections exception, and in another one (which is the one I focus on) I simply receive 400 Bad Request although the exact same request returns 200 on my PC.

I noticed that there are actually two different implementations: On PC I'm using sun.net.www.protocol.https.DelegateHttpsURLConnection, and on Android it's com.android.okhttp.internal.http.HttpURLConnectionImpl. Object conn looks a bit different during runtime. However, I failed to find a meaningful difference (if needed, I can post the whole content of these objects); I found one difference in the cookies set in the first GET, but a manual manipulation resulted in the same 400.

I tried to capture the Android output request using Wireshark, but the result is encrypted and I didn't manage to decrypt it.

I basically thought of two possible scenarios:

  • Find the difference(s) between those implementations and act accordingly.
  • Find a way to use the sun.net.www.protocol.https.DelegateHttpsURLConnection on Android.

So far I didn't manage to figure out any of these. Is there any known difference/issue with these implementations? Is there a way to run the native Java library on Android? Any help will be appreciated, thanks.

Neria Nachum
  • 1,519
  • 1
  • 20
  • 37
  • log or verify that no headers were duped or extra headers exist . http://stackoverflow.com/questions/22173517/httpurlconnection-wire-logging-in-android AND try encoding the raw bytes in the body with a 'writer' . Last point is like converting byte array to stream. – Robert Rowntree Jul 03 '16 at 14:06
  • Logging the headers before getting the output stream results in an exception because it implictly gets the stream when requesting headers. The same thing happened to me when checking similar things in the 'Watches' window. – Neria Nachum Jul 03 '16 at 14:28
  • Regarding your second advice, I'm currently doing this only for the input parameters of the POST request (see variable `postParams`). Using JSoup I'm getting all elements tagged `input` and concatenate them after encoding with `URLEncoder.encode`. Do you think there's anything else to do in this subject? – Neria Nachum Jul 03 '16 at 14:31
  • try review of 'post' here : https://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection – Robert Rowntree Jul 03 '16 at 14:39

1 Answers1

0

Yes, there are some difference between those SDKs, the desktop is the Oracle sdk and on Android is the one published by Google.

Ony way, when doing http requests on Android it's recommended using Retrofit or Volley

shem
  • 4,686
  • 2
  • 32
  • 43
  • I focused on Volley in the last couple of days and so far I faced and didn't manage to solve a problem in the same phase, and I'm afraid that it actually relies on HttpUrlConnection so it's pretty much a dead end... – Neria Nachum Jul 05 '16 at 13:19
  • Do you know if it's in any way possible to use the Oracle SDK? I read about NDK which enables to use C and C++, maybe there's something similar for native Java. – Neria Nachum Jul 05 '16 at 13:58
  • No, you can't use the Oracle SDK – shem Jul 05 '16 at 19:17