HttpURLConnection.getResponseCode () freezes execution / does not disconnect

I am writing an Android application that connects to a cpanel server page (Apache 2.2.22) that is password protected. When the authentication details are correct, I have no connection problems. However, when the credentials are incorrect, my Android app seems to freeze in the HttpURLConnection.getResponseCode() method. Logs on the server show hundreds of requests sent from my Android device, all return 401 as expected, but for some reason this is not reflected in my application.

Here is my code executed from AsyncTask:

  @Override protected Integer doInBackground(String... bookInfoString) { // Stop if cancelled if(isCancelled()){ return null; } Log.i(getClass().getName(), "SendToDatabase.doInBackground()"); String apiUrlString = getResources().getString(R.string.url_vages_library); try{ NetworkConnection connection = new NetworkConnection(apiUrlString); connection.appendPostData(bookInfoString[0]); int responseCode = connection.getResponseCode(); Log.d(getClass().getName(), "responseCode: " + responseCode); return responseCode; } catch(IOException e) { return null; } } 

This code uses my own NetworkConnection class, which is just the base wrapper class around HttpURLConnection, to avoid code repeating. Here he is:

 public class NetworkConnection { private String url; private HttpURLConnection connection; public NetworkConnection(String urlString) throws IOException{ Log.i(getClass().getName(), "Building NetworkConnection for the URL \"" + urlString + "\""); url = urlString; // Build Connection. try{ URL url = new URL(urlString); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setReadTimeout(1000 /* 1 seconds */); connection.setConnectTimeout(1000 /* 1 seconds */); } catch (MalformedURLException e) { // Impossible: The only two URLs used in the app are taken from string resources. e.printStackTrace(); } catch (ProtocolException e) { // Impossible: "GET" is a perfectly valid request method. e.printStackTrace(); } } public void appendPostData(String postData) { try{ Log.d(getClass().getName(), "appendPostData() called.\n" + postData); Log.d(getClass().getName(), "connection.getConnectTimeout(): " + connection.getConnectTimeout()); Log.d(getClass().getName(), "connection.getReadTimeout(): " + connection.getReadTimeout()); // Modify connection settings. connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/json"); // Get OutputStream and attach POST data. OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); writer.write(postData); if(writer != null){ writer.flush(); writer.close(); } } catch (SocketTimeoutException e) { Log.w(getClass().getName(), "Connection timed out."); } catch (ProtocolException e) { // Impossible: "POST" is a perfectly valid request method. e.printStackTrace(); } catch (UnsupportedEncodingException e) { // Impossible: "UTF-8" is a perfectly valid encoding. e.printStackTrace(); } catch (IOException e) { // Pretty sure this is impossible but not 100%. e.printStackTrace(); } } public int getResponseCode() throws IOException{ Log.i(getClass().getName(), "getResponseCode()"); int responseCode = connection.getResponseCode(); Log.i(getClass().getName(), "responseCode: " + responseCode); return responseCode; } public void disconnect(){ Log.i(getClass().getName(), "disconnect()"); connection.disconnect(); } } 

And finally, here is some of the logcat logs:

 05-03 11:01:16.315: D/vages.library.NetworkConnection(3408): connection.getConnectTimeout(): 1000 05-03 11:01:16.315: D/vages.library.NetworkConnection(3408): connection.getReadTimeout(): 1000 05-03 11:01:16.585: I/vages.library.NetworkConnection(3408): getResponseCode() 05-03 11:04:06.395: I/vages.library.MainActivity$SendToDatabase(3408): SendToDatabase.onPostExecute(null) 

You can see that the method seems to just return null after a random time. The longest I expected was exactly 15 minutes. There are also several memory logs (GC_CONCURRENT) from dalikvm between the last two informational logs that I skipped.

I must also say that at the moment I am not using https, although I do not believe that this should cause any problems. I would be very grateful for any feedback on this, be it a complete answer or just a comment telling me that the problem is not , as I'm still not sure if this problem is server-side or client -side.

Thanks a lot, William

EDIT: I forgot to mention this before, I am attaching my authentication credentials with my own java.net.Authenticator :

 public class CustomAuthenticator extends Authenticator { Context mContext; public CustomAuthenticator(Context context){ super(); mContext = context; } @Override protected PasswordAuthentication getPasswordAuthentication() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); String username = sharedPreferences.getString(SettingsActivity.KEY_USERNAME_PREFERENCE, null); String password = sharedPreferences.getString(SettingsActivity.KEY_PASSWORD_PREFERENCE, null); return new PasswordAuthentication(username, password.toCharArray()); } } 

which I set in the onCreate() method of activity:

 Authenticator.setDefault(new CustomAuthenticator(mContext)); 

In addition, I used curl to request a password protected resource, and got 401 as expected. I now assume that the problem is with the client side.

+4
source share
3 answers

It seems that issue using Authenticator in POST connections. It is quite old, so I don’t know if it still exists.

I would try two things:

  • Add a log line to getPasswordAuthentication Authenticator to see if it is actually called. If nothing is printed, you should check to see if you added Authenticator by default before calling it. You say you do it in onCreate() , so it should be good, but it’s good to be sure.
  • Avoid using Authenticator (at least for testing purposes) and send authentication information directly in the HTTP request. I usually do this:

     String auth = user + ":" + pass; conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("Authorization", "Basic " + Base64.encode(auth.getBytes())); // Set other parameters and read the result... 
+3
source

The problem was that the 401 Unauthorized status is sent when the Authorization header is missing and when the credentials contained in the header are incorrect. Therefore, my application constantly sent the same request again and again to no avail. So I found a workaround to the problem by adding a counter to my CustomAuthenticator :

 public class CustomAuthenticator extends Authenticator { public static int RETRIES = 3; int mRetriesLeft; Context mContext; public CustomAuthenticator(Context context){ super(); mRetriesLeft = RETRIES; mContext = context; } @Override protected PasswordAuthentication getPasswordAuthentication() { Log.i(getClass().getName(), "getPasswordAuthentication() - mCounter: " + mRetriesLeft); if(mRetriesLeft > 0){ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); String username = sharedPreferences.getString(SettingsActivity.KEY_USERNAME_PREFERENCE, null); String password = sharedPreferences.getString(SettingsActivity.KEY_PASSWORD_PREFERENCE, null); mRetriesLeft--; return new PasswordAuthentication(username, password.toCharArray()); } else { Log.w(getClass().getName(), "No more retries. Returning null"); mRetriesLeft = RETRIES; return null; } } public void reset(){ mRetriesLeft = RETRIES; } } 

I must say, however, that I do not like this decision, and therefore I did not accept it. You must remember to reset the counter when you make a new request (I do this in AsyncTask.onPreExecute() ), otherwise every third request will fail. In addition, I am sure that there should be an own way to do this, although after cleaning the documentation I can not find it. I would still be very grateful if anyone could point this out to me.

+2
source

I don’t know if I’m right or wrong, but my solution worked for me all day without a glitch.

Try to do it

 byte[] buf = new byte[4096]; Inputstream is; do { http conn code etc; is=conn.getInputStream(); if(is.read(buf)==0) { flag=1; } //u can either is.close(); or leave as is //code int serverResponseCode = connection.getResponseCode(); String serverResponseMessage = connection.getResponseMessage(); conn.disconnect(); } while(flag==1); 
0
source

Source: https://habr.com/ru/post/1478971/


All Articles