How to check if OkHttp3 request timed out? - android

I am making an HTTP GET request to a Web Server using the code below:
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
}
According to OkHttp document as shown below, okhttp3 execute calls will throw IOException if the request could not be executed due to cancellation, a connectivity problem or timeout.
Throws:
IOException - if the request could not be executed due to cancellation, a connectivity problem or timeout. Because networks can fail during an exchange, it is possible that the remote server accepted the request before the failure.
I would like to know if there's a way to know if IOException was due to the request getting timed out?

Although I could not find any information about timeout exception in okHttp3 documentation, looking at tests here shows that a SocketTimeoutException exception will be raised in case of timeouts.
So, to answer my own question, we can catch SocketTimeoutException first in order to know if IOException was due to the request getting timed out like below:
try {
// make http request
} catch (SocketTimeoutException e) {
// request timed out
} catch (IOException e) {
// some other error
}

Related

Making GET Request after a POST Request in the same Session with OkHttp in Android Studio

I am trying to retrieve some JSON data using OkHttp in Android Studio from the URL: www.duolingo.com/vocabulary/overview
Before I can get the data using this URL, it requires me to Login into the Duolingo server first (which makes sense since I want it to return data from my profile) so I make a POST request with my credentials using OkHttp. This is my code to achieve that:
OkHttpClient client = new OkHttpClient();
String postUrl = "https://www.duolingo.com/2017-06-30/login?fields=";
String getUrl = "https://www.duolingo.com/vocabulary/overview";
String credentials = "{\"identifier\": \"something#email.com\", \"password\": \"Password\"}";
RequestBody body = RequestBody.create(credentials, MediaType.parse("application/json; charset=utf-8"));
Request request = new Request.Builder()
.url(postUrl)
.post(body)
.build();
client.newCall(request).enqueue(new Callback()
{
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e)
{
Log.i("TAG", "ERROR - " + e.getMessage());
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException
{
if (response.isSuccessful())
{
Log.i("LOG", "LOGGED IN");
}
else
{
Log.i("LOG", "FAILED - " + response.toString());
}
}
});
The Response is successful and I get a 200 Response Code.
Now since I have Logged In, I want to make a GET Request to the URL mentioned above to get the JSON Data. Problem is I do not know how to make a GET Request in succession to the POST Request I just made. OkHttp treats the 2 consecutive requests as separate while I want them to be treated as the same session.
Someone told me Cookies can help but I am totally oblivious to that and how they work. All help is appreciated!

How To Write Retry Interceptor For Data Stream In OkHttp?

Or thinking the interceptor for this scenario applicable ?
Our app using OkHttp for downloading files (new version of app, daily databases etc.)
Sometimes server fails just while the app streaming bytes (btw the problem is, recvfrom failed: ECONNRESET)
So to fix this case i just wanted to write OkHttp retry interceptor. But seems this is appropriate for operations which aren't streaming.
Is there a solution(like interceptor) to handle this case ?
To make more clear exposition
0%==============================100% (Just started streaming)
0%==============================100% (10% completed)
0%==============================100% (20% completed)
0%==============================100% (ECONNRESET - Connection reset by peer)
At just this point, streaming gets stopped. The thing i'm wishing from OkHttp is recognizing this situation then starting the stream from scratch (not from 20%)
Related code here, pay attention to comments
Call call = client.newCall(new Request.Builder().url(url).get().build());
Response response = call.execute();
// PROBLEM DOES NOT OCCUR THERE
// PROBLEM DOES NOT OCCUR THERE
// PROBLEM DOES NOT OCCUR THERE
if (response.code() == 200 || response.code() == 201) {
InputStream inputStream = null;
try {
long downloaded = 0;
byte[] buff = new byte[1024 * 4];
inputStream = response.body().byteStream();
long target = response.body().contentLength();
while (true) {
// EXCEPTION OCCURS THERE
// EXCEPTION OCCURS THERE
// EXCEPTION OCCURS THERE
int read = inputStream.read(buff);
if (read == -1) {
break;
}
downloaded += read;
}
...
} catch (IOException e) {
// EXCEPTION SAYS
// ECONNRESET - Connection reset by peer
...
}
}
You can write a custom Interceptor like below:
OkHttp has Interceptors. You need a custom Interceptor like one below:
public class CustomResponseInterceptor implements Interceptor {
private final String TAG = getClass().getSimpleName();
#Override
public Response intercept(Object object) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.code() != 200//Your error code here,) {
//Cancel your Request here
return something;
}
Log.d(TAG, "INTERCEPTED:$ " response.toString());
return response;
}
The code shown is extracted from this Medium Article on Interceptors.
You can also look at this library which implements Retry Interceptors, But you can modify it for your use.
When ECONNRESET - Connection reset by peer occurs why don't you cancel your ongoing call in your catch block and start a new network call for the same file
catch (IOException e) {
// EXCEPTION SAYS
// ECONNRESET - Connection reset by peer
...
call.cancel();//something like this
startDownloadingFromScratch();//new network request to start from scratch
}
Try this.
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = chain.proceed(request);
int tryCount = 0;
while (!response.isSuccessful() && tryCount < 3) {
Log.d("intercept", "Request is not successful - " + tryCount);
tryCount++;
// retry the request
response = chain.proceed(request);
}
// otherwise just pass the original response on
return response;
}
});
set this client as your retrofit client.
new Retrofit.Builder()
...other codes
.client(client)
...other codes
.build();
Good luck.

Retrofit change read timeout for one api call

I want to change the read timeout on OkHttp client using retrofit for one api call. To be clear, I have one end point that can take very long, I need to increase it's timeout and only the timeout for that one api call. Is there a way I can do this through annotations? Is there a way I can do this without changing the timeouts for the rest of the app?
I'm facing a similar situation. I solve my problem providing two Api instances in my ApiModule, each one with your own OkHttpClient. Use #Named to identify each one.
I tried to avoid providing two instances only for a timeout configuration, seems a little strange for me, but since my API instance is a singleton (for performance), I could not see other solution.
You can actually do a per call configuration
Copy the default builder and make another client
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://blah_blah_api.com/") // This URL is served with a 1 second delay.
.build();
try {
// Copy to customize OkHttp for this request.
OkHttpClient copy = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Response response = copy.newCall(request).execute();
System.out.println("Response 1 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 1 failed: " + e);
}
try {
// Copy to customize OkHttp for this request.
OkHttpClient copy = client.newBuilder()
.readTimeout(3000, TimeUnit.MILLISECONDS)
.build();
Response response = copy.newCall(request).execute();
System.out.println("Response 2 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 2 failed: " + e);
}
}

Retrofit2 204 No Content has content exception

I get from server empty json ("{}") as a delete response with code 204.
In okhttp3.internal.http.HttpEngine class there is this annoying thing that gets thrown:
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
If you try return something without content (server side) in headers still Content-Length greater than 0;
Any non server side ideas how to solve this issue?
You can trap the ProtocolException in an interceptor and return a placeholder 204 Response. Caveats with this approach -- 1) you may end up trapping other protocol errors (too many redirects, etc). If this is a concern, you could compare e.getMessage() to okhttp's exception message and rethrow the exception if there is not a match. 2) you still don't have access to the original response, so if you are out of luck if you need to inspect any of the returned headers.
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addNetworkInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response response;
try {
response = chain.proceed(chain.request());
} catch (ProtocolException e) {
response = new Response.Builder()
.request(chain.request())
.code(204)
.protocol(Protocol.HTTP_1_1)
.build();
}
return response;
}
});
This can be avoided if used differently a bit. Instead of:
#DELETE("vehicles/{id}/")
Observable<Response<BaseResponse>> deleteVehicle(#Header("Authorization") String token, #Path("id") Long vehicleId);
I use:
#HTTP(method = "DELETE", path = "vehicles/{id}/", hasBody = true)
Observable<Response<BaseResponse>> deleteVehicle(#Header("Authorization") String token, #Path("id") Long vehicleId);

Android HttpCleint works on 2.2 but not on 4

I have a method (below) to get data from web services in my android program. It works fine with Android 2.2. But in 4.x it throws an exception with message "Error Requesting API". Not sure which part is causing the exception in my code and why only in 4.x Can someone give me some pointers?
public static synchronized String performHTTPRequest(HttpUriRequest request)
throws ApiException, ParseException, IOException {
HttpClientWrapper cli=new HttpClientWrapper();
HttpClient client;
try {
client=cli.getClient();
HttpResponse response = client.execute(request);
// Check if server response is valid
StatusLine status = response.getStatusLine() ;
if (status.getStatusCode() != HTTP_STATUS_OK) {
throw new ApiException("Invalid response from server: " + status.toString());
}
return EntityUtils.toString(response.getEntity());
} catch (MalformedURLException e) {
throw new Error(e);
} catch (Exception cnrce) {
throw new ApiException("Error requesting API:");
}
}
In 4.0 or the verions above honeycomb you can not perform any http request on ui thread.
You need to perform these in asynctask.
EDIT:
Documentation
Code example for asynctask

Categories

Resources