In light of the recent POODLE vulnerability, the servers that I talk to in my Android app are disabling SSL 3.
The code where I make my HTTP requests looks something like this
AndroidHttpClient c = AndroidHttpClient.newInstance("My Client/1.0");
try {
SyncBasicHttpContext httpcontext = getHttpContext();
HttpResponse jsonResponse = c.execute(request, httpcontext);
Status status = jsonResponse.getStatus();
} catch (Exception e) {
// ...etc
}
Is there anything I will need to do to my code to continue to be able to talk to the severs?
Related
Hello I am trying to begin an Android app in Kotlin. I have been trying to find a way to simply send and receive data from an android app to a simple HTTP server I made on my computer. The idea I am hoping is just make an app with a text box on it and a send button. When I press the send button it sends it to the simple http server (whether I use IP address or URL isn't Important)
This is just simply a proof of concept that I can make my own simple HTTP Server and get the app to send and receive from it.
I am essentially trying to do a server-client architecture between an android app and a computer. I can get this concept to work between two computer applications (python, java, C++ etc) but not how to do it in android. I keep looking for other answers on here but still come up short.
to be specific do I need enable certain features within the configurations file or a library that will allow the task to be done in the background?
Thank you for your help in advance.
If you want to connect of server for sending requests (POST and GET) then follow this code
public void callApi(String urls,String calledFor) {
try {
HttpGet httppost = new HttpGet(urls);
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httppost);
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
String data = EntityUtils.toString(entity);
JSONObject jsono = new JSONObject(data);
JSONObject json = jsono.getJSONObject("data");
}
} catch (ParseException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
ktor is a wonderful HTTP client that you can use for Android. It is developed by JetBrains, who are also authors of Kotlin, so it takes advantage of the features of the language:
You will need to add the ktor client for Android in build.gradle:
implementation "io.ktor:ktor-client-android:$ktor_version"
Then set up your client like this:
val client = HttpClient(Android) {
// setting these properties is optional; you don't need to if you wish to use the defaults
engine {
connectTimeout = 100_000
socketTimeout = 100_000
}
}
At last you can make HTTP requests like so (example from here): (the use method is to automatically close it at the end, make sure you read about releasing resources)
val resp: String = client.use {
val htmlContent = it.get<String>("https://en.wikipedia.org/wiki/Main_Page")
println(htmlContent)
// same as
val content: String = it.get("https://en.wikipedia.org/wiki/Main_Page")
content
}
Post Api request Using okHttp in Kotlin (Android)
follow steps
You will need to add the dependencies in build.gradle(:app)
implementation("com.squareup.okhttp3:okhttp:4.9.0")
request code here
fun runPostApi() {
var url = "https://reqres.in/api/users"
// add parameter
val formBody = FormBody.Builder().add("name", " Parker")
.build()
// creating request
var request = Request.Builder().url(url)
.post(formBody)
.build()
var client = OkHttpClient();
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
println(response.body?.string())
}
override fun onFailure(call: Call, e: IOException) {
println(e.message.toString())
}
})}
for more information link here below
https://square.github.io/okhttp/recipes/
Running the code below I get "error" : "unauthorized_client". Need help pinpoint what I have done wrong.
I Want to use this in an Android app to enable Google Talk (xmpp is there already XABBER fork)
Reading much about how to do this and now come the Google Api Console setup.
I have chosen
- Client ID for installed applications
- Installed application type = Other
- Enabled only the Google Play Android Developer API (green switch)
I get the authentication token from oauth2:https://www.googleapis.com/auth/googletalk. (user approval screen)
What bugs me I dont enter the SHA1 fingerprint or package name for "Other" so maybe that's the problem
code:
HttpClient client1 = new DefaultHttpClient();
HttpPost request1 = new HttpPost("https://accounts.google.com/o/oauth2/token" );
request1.setHeader("Content-type", "application/x-www-form-urlencoded");
//Please make this custom with you're credentials
String requestBody1 =
"code="+authToken+
"&client_id=749825062016ia.apps.googleusercontent.com"+
"&client_secret=jQ1nUrAYUIBUf6hN5pwPE" +
"&redirect_uri=urn:ietf:wg:oauth:2.0:oob" +
"&grant_type=authorization_code";
try {
request1.setEntity(new StringEntity(requestBody1));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
/* Checking response */
try {
HttpResponse response = client1.execute(request1);
String results = "ERROR";
results = EntityUtils.toString(response.getEntity());
LogManager.i("STACK", "Response::" + results);
} catch (IOException e) {
e.printStackTrace();
}
I think you should have registered your application with type 'Android'. Then you will be asked to provide information (such as package name and certificate) that will allow the Android platform to recognize your application.
Here's the question in simplest way.
I create a HTTPS connection to my server through proxy using HttpUrlConnection Object.
My proxy closes the connection but my code still tries to reuse the same connection. And so I get EOFException.
How do I handle such cases?
I'd recommend disabling the http.keepalive system property. The performance section of the documentation indicates that socket connections will be reused when possible. It sounds like it is trying to reuse the connection, but the proxy has already closed it. On this post, Darrell also indicates that changing the system property solved his problem.
System.setProperty("http.keepAlive", "false");
Turns out they've fixed this issue in Android on Jan 8th [1]. The fix basically marks the old connection as recycled and internally retries the request.
To fix this for now, I would suggest retrying requests if an EOFException is encountered with a retry limit to prevent stackoverlows.
https://android.googlesource.com/platform/libcore/+/19aa40c81c48ff98ccc7272f2a3c41479b806376
I had this problem with normal HTTP connections as well. The first request went OK, the second failed with an EOFException.
Eventuelly I fixed it by removing...
urlConnection.setChunkedStreamingMode(0);
...from the HttpUrlConnection.
I could be that the webserver I'm calling can't handle chuncked data well. Don't know.
If you don't want to reuse the connection then release it.
finally {
urlConnection.disconnect();
}
You can use this method to pick data from server then you convert the inputs trim to string then you can parse for further use.`
public static InputStream getUrlData(final String url)
throws URISyntaxException, ClientProtocolException, IOException {
final DefaultHttpClient client = new DefaultHttpClient();
final HttpGet method = new HttpGet(new URI(url));
final HttpResponse res = client.execute(method);
return res.getEntity().getContent();
}
Maybe httpClient "has more bugs" and is deprecated, but this problem with JellyBean is a showstopper. I am using Ksoap2 so I tried all the suggested answers that I could.
System.setProperty("http.keepAlive", "false");
httpTransportSE.getServiceConnection().setRequestProperty("Connection", "close");
httpTransportSE.getServiceConnection().disconnect();
Nothing worked - my solution was to rollback the version of Ksoap2 I'm using from 3.1.1 to 2.6.5. Using 2.6.5 the problem is substantially reduced. Still testing but maybe even solved.
I found that retrying the connection fixes the issue as seen here: https://stackoverflow.com/a/20302767/2520390
Make sure you close off the connection before your recursive call.
Also, I added the following to the connection to close the connection, though I'm not sure if it helps:
if (retries > 0) {
connection.setRequestProperty("Connection", "close");
}
You shouldn't be attempting to reuse the same HttpURLConnection instance. The docs in the very bottom line of the "Class Overview" say
Each instance of HttpURLConnection may be used for one
request/response pair.
Keep-Alive connections work at a different level, see the disconnect docs:
http://developer.android.com/reference/java/net/HttpURLConnection.html#disconnect()
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should always use a fresh HttpURLConnection and let the socket pool handle re-use.
There were apparently bugs with keep-alive connections pre-Froyo (2.2) so it is recommended to disable keep-alive on those old devices.
In my case the EOFException was caused by my server not sending a full response, see the details here: https://stackoverflow.com/a/27845172/2335025
You shouldn't be attempting to reuse the same HttpURLConnection instance. The docs in the very bottom line of the "Class Overview" say
Each instance of HttpURLConnection may be used for one
request/response pair.
Keep-Alive connections work at a different level, see the disconnect docs:
http://developer.android.com/reference/java/net/HttpURLConnection.html#disconnect()
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should always use a fresh HttpURLConnection and let the socket pool handle re-use. There are perhaps issues if it tries to reuse a socket that has been closed by the server, which the answers to this question deal with: Android HttpUrlConnection EOFException
There were apparently bugs with keep-alive connections pre-Froyo (2.2) so it is recommended to disable keep-alive on those old devices.
In my case the EOFException was caused by my server not sending a full response, see the details here: https://stackoverflow.com/a/27845939/2335025
if (Build.VERSION.SDK != null
&& Build.VERSION.SDK_INT > 13) {
con.setRequestProperty("Connection", "close");
}
Try this code:`
Httppost method:
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC);
HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC);
HttpClient client = new DefaultHttpClient(httpParams);
HttpPost request = new HttpPost("put_url");
request.setHeader("Content-Type", "application/xml");
String file = resourceXml();
StringEntity se = null;
try {
se = new StringEntity(file);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
se.setContentEncoding("UTF-8");
se.setContentType("application/xml");
request.setEntity(se);
HttpResponse response = null;
try {
response = client.execute(request);
} catch (ClientProtocolException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
HttpEntity entity = response.getEntity();
InputStream is = null;
try {
is = entity.getContent();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String _response = convertStreamToString(is);
Log.i(TAG, "Response:" + _response);
// Check if server response is valid code
int res_code = response.getStatusLine().getStatusCode();
Log.i(TAG, "status_code" + res_code);
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
`
to convert stream to string:`
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is),
8192);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append((line + "\n"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}`
I've got a class:
public class WebReader implements IWebReader {
HttpClient client;
public WebReader() {
client = new DefaultHttpClient();
}
public WebReader(HttpClient httpClient) {
client = httpClient;
}
/**
* Reads the web resource at the specified path with the params given.
* #param path Path of the resource to be read.
* #param params Parameters needed to be transferred to the server using POST method.
* #param compression If it's needed to use compression. Default is <b>true</b>.
* #return <p>Returns the string got from the server. If there was an error downloading file,
* an empty string is returned, the information about the error is written to the log file.</p>
*/
public String readWebResource(String path, ArrayList<BasicNameValuePair> params, Boolean compression) {
HttpPost httpPost = new HttpPost(path);
String result = "";
if (compression)
httpPost.addHeader("Accept-Encoding", "gzip");
if (params.size() > 0){
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
try {
HttpResponse response = client.execute(httpPost);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
if (entity.getContentEncoding() != null
&& "gzip".equalsIgnoreCase(entity.getContentEncoding()
.getValue()))
result = uncompressInputStream(content);
else
result = convertStreamToString(content);
} else {
Log.e(MyApp.class.toString(), "Failed to download file");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private String uncompressInputStream(InputStream inputStream)
throws IOException {...}
private String convertStreamToString(InputStream is) {...}
}
I cannot find a way to test it using a standard framework. Especially, I need to simulate total internet lost from inside the test.
There are suggestions to manually turn the Internet in the emulator off while performing the test. But it seems to me as not quite a good solution, because the automatic tests should be... automatic.
I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.
The Robolectric framework allows the developers to test Http connection as far as I know. But I guess there is some way to write such a test without using so big additional framework.
So are there any short and straightforward ways of unit testing classes that use HttpClient? How did you solve this in your projects?
I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.
I am a little bit confuse about this statement. From the question title, you are asking about unit-testing httpClint, by mocking a FakeHttpClient may help you unit-testing other part of app except httpClient, but doesn't help anything for unit-testing httpClient. What you need is a FakeHttpLayer for unit-testing httpClient (no remote server, network requires, hence unit-testing).
HttpClient Dummy Test:
If you only need examine app behavior in the situation that internet is lost, then a classic Android Instrument Test is sufficient, you can programmatically turn the Internet in the emulator off while performing the test:
public void testWhenInternetOK() {
... ...
webReader.readWebResource();
// expect HTTP 200 response.
... ...
}
public void testWhenInternetLost() {
... ...
wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(false);
webReader.readWebResource();
// expect no HTTP response.
... ...
}
This requires the remote http server is completely setup and in a working state, and whenever you run your test class, a real http communication is made over network and hit on http server.
HttpClient Advanced Test:
If you want to test app behavior more precisely, for instance, you want to test a http call in you app to see if it is handle different http response properly. the Robolectric is the best choice. You can use FakeHttpLayer and mock the http request and response to whatever you like.
public void setup() {
String url = "http://...";
// First http request fired in test, mock a HTTP 200 response (ContentType: application/json)
HttpResponse response1 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 200, null);
BasicHttpEntity entity1 = new BasicHttpEntity();
entity1.setContentType("application/json");
response1.setEntity(entity1);
// Second http request fired in test, mock a HTTP 404 response (ContentType: text/html)
HttpResponse response2 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 404, null);
BasicHttpEntity entity2 = new BasicHttpEntity();
entity2.setContentType("text/html");
response2.setEntity(entity2);
List<HttpResponse> responses = new ArrayList<HttpResponse>();
responses.add(response1);
responses.add(response2);
Robolectric.addHttpResponseRule(new FakeHttpLayer.UriRequestMatcher("POST", url), responses);
}
public void testFoo() {
... ...
webReader.readWebResource(); // <- a call that perform a http post request to url.
// expect HTTP 200 response.
... ...
}
public void testBar() {
... ...
webReader.readWebResource(); // <- a call that perform a http post request to url.
// expect HTTP 404 response.
... ...
}
Some pros of using Robolectric are:
Purely JUnit test, no instrument test so don't need start emulator (or real device) to run the test, increase development speed.
Latest Robolectric support single line of code to enable/disable FakeHttpLayer, where you can set http request to be interpreted by FakeHttpLayer (no real http call over network), or set the http request bypass the FakeHttpLayer(perform real http call over network). Check out this SO question for more details.
If you check out the source of Robolectric, you can see it is quite complex to implement a FakeHtppLayer properly by yourself. I would recommend to use the existing test framework instead of implementing your own API.
Hope this helps.
I'm trying to write application for Android to access Google Tasks. I decided to use ClientLogin authorization method.
I'm getting ClientLogin "Auth" marker from first POST request. Then i try to retrieve a user's task lists with GET request. I wrote the following code for this:
String requestString = "https://www.googleapis.com/tasks/v1/users/#me/lists";
String resultString = "";
try {
URLConnection connection1 = null;
URL url = new URL(requestString);
connection1 = url.openConnection( );
HttpURLConnection httpsConnection1 = (HttpURLConnection)connection1;
httpsConnection1.setRequestMethod("GET");
httpsConnection1.setRequestProperty("Authorization", "GoogleLogin auth="+authkeyString);
httpsConnection1.setDoInput(true);
httpsConnection1.connect();
int responseCode = httpsConnection1.getResponseCode();
System.out.println(responseCode);
if (responseCode == HttpsURLConnection.HTTP_OK) {
InputStream in = httpsConnection1.getInputStream();
InputStreamReader isr = new InputStreamReader(in, "UTF-8");
StringBuffer data = new StringBuffer();
int c;
while ((c = isr.read()) != -1){
data.append((char) c);
}
resultString = new String (data.toString());
}
else{
resultString = "Errror - connection problem";
}
}
httpsConnection1.disconnect();
}
catch (MalformedURLException e) {
resultString = "MalformedURLException1:" + e.getMessage();
}
catch (IOException e) {
resultString = "IOException1:" + e.getMessage();
}
Here is "authkeyString" - string variable with authorization marker.
When i run application under real Android device i receive: "IOException:SSL handshake failure: Failure is ssl library, usually a protocol error ..... "
Also i tried to run this code from simple java application from desktop:
Exception in thread "main" java.lang.IllegalArgumentException: Illegal character(s) in message header value: GoogleLogin auth=DQ ..... UT
at sun.net.www.protocol.http.HttpURLConnection.checkMessageHeader(HttpURLConnection.java:428)
at sun.net.www.protocol.http.HttpURLConnection.isExternalMessageHeaderAllowed(HttpURLConnection.java:394)
at sun.net.www.protocol.http.HttpURLConnection.setRequestProperty(HttpURLConnection.java:2378)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestProperty(HttpsURLConnectionImpl.java:296)
at Tasks.main(Tasks.java:81)
ClientLogin with username / password
If you want to use ClientLogin with the Google APIs Client Library for Java , you'll need to setup a HttpRequestFactory that supports authentication.
private static HttpTransport transport = new ApacheHttpTransport();
public static HttpRequestFactory createRequestFactory(
final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
GoogleHeaders headers = new GoogleHeaders();
headers.setApplicationName("MyApp/1.0");
request.headers=headers;
try {
authorizeTransport(request);
} catch (Exception e) {
e.printStackTrace();
}
}
});
Notice the authorizeTransport method, that will basically authorize the request. The authorizeTransport looks like this:
private void authorizeTransport(HttpRequest request) throws HttpResponseException, IOException {
// authenticate with ClientLogin
ClientLogin authenticator = new ClientLogin();
authenticator.authTokenType = Constants.AUTH_TOKEN_TYPE;
authenticator.username = Constants.USERNAME;
authenticator.password = Constants.PASSWORD;
authenticator.transport = transport;
try {
Response response = authenticator.authenticate();
request.headers.authorization=response.getAuthorizationHeaderValue();
} catch (HttpResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
You basically setup a ClientLogin authentication method by providing a username/passsword. The authenticate method will authenticate based on the provided values and returns a response object that can be added to the HTTP header to provide ClientLogin authentication.
Android AccountManager
In order to integrate with the Android AccountManager (avoiding the android user to type in his username / password) , you can find some sample code here http://code.google.com/p/google-api-java-client/wiki/AndroidAccountManager
The fact that the user doesn't need to key in his username/passwords adds to the users comfort level, but the solution remains relatively insecure.
I would strongly suggest doing the following :
Use a client library
I would suggest moving to Google APIs Client Library for Java for this type of interaction. It's Android compatible java client library for all kinds of Google APIs.
You don't want to be bothered with implementing low level HTTP, security and JSON plumbing.
The Google Task API Developers guide also mentions the same library. The library will take care of the authentication for you. If you want to use ClientLogin, all you'll have to do is specify a username/password, or integrate with the Android AccountManager.
Avoid using ClientLogin
ClientLogin is considered insecure, and a number of security holes have been found regarding this authentication mechanism. Google also doesn't recommend it. However, should you decide to continue using ClientLogin, the Google APIs Client Library for Java does support it.