HTTP Caching with Retrofit 2.0.x - android

I'm trying to cache some responses in my app using Retrofit 2.0, but I'm missing something.
I installed a caching file as follows:
private static File httpCacheDir;
private static Cache cache;
try {
httpCacheDir = new File(getApplicationContext().getCacheDir(), "http");
httpCacheDir.setReadable(true);
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
HttpResponseCache.install(httpCacheDir, httpCacheSize);
cache = new Cache(httpCacheDir, httpCacheSize);
Log.i("HTTP Caching", "HTTP response cache installation success");
} catch (IOException e) {
Log.i("HTTP Caching", "HTTP response cache installation failed:" + e);
}
public static Cache getCache() {
return cache;
}
which creates a file in /data/user/0/<PackageNmae>/cache/http
, then prepared a network interceptor as follows:
public class CachingControlInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Add Cache Control only for GET methods
if (request.method().equals("GET")) {
if (ConnectivityUtil.checkConnectivity(getContext())) {
// 1 day
request.newBuilder()
.header("Cache-Control", "only-if-cached")
.build();
} else {
// 4 weeks stale
request.newBuilder()
.header("Cache-Control", "public, max-stale=2419200")
.build();
}
}
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=86400")
.build();
}
}
my Retrofit and OkHttpClient instance:
OkHttpClient client = new OkHttpClient();
client.setCache(getCache());
client.interceptors().add(new MainInterceptor());
client.interceptors().add(new LoggingInceptor());
client.networkInterceptors().add(new CachingControlInterceptor());
Retrofit restAdapter = new Retrofit.Builder()
.client(client)
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
productsService = restAdapter.create(ProductsService.class);
where ProductsService.class contains:
#Headers("Cache-Control: max-age=86400")
#GET("categories/")
Call<PagedResponse<Category>> listCategories();
and
Call<PagedResponse<Category>> call = getRestClient().getProductsService().listCategories();
call.enqueue(new GenericCallback<PagedResponse<Category>>() {
// whatever
// GenericCallback<T> implements Callback<T>
}
});
The question here is: How to make it access cached responses when device being offline?
Header of backend response are:
Allow → GET, HEAD, OPTIONS
Cache-Control → max-age=86400, must-revalidate
Connection → keep-alive
Content-Encoding → gzip
Content-Language → en
Content-Type → application/json; charset=utf-8
Date → Thu, 17 Dec 2015 09:42:49 GMT
Server → nginx
Transfer-Encoding → chunked
Vary → Accept-Encoding, Cookie, Accept-Language
X-Frame-Options → SAMEORIGIN
x-content-type-options → nosniff
x-xss-protection → 1; mode=block

Finally I get the answer.
Network Interceptor should be as follow:
public class CachingControlInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Add Cache Control only for GET methods
if (request.method().equals("GET")) {
if (ConnectivityUtil.checkConnectivity(YaootaApplication.getContext())) {
// 1 day
request = request.newBuilder()
.header("Cache-Control", "only-if-cached")
.build();
} else {
// 4 weeks stale
request = request.newBuilder()
.header("Cache-Control", "public, max-stale=2419200")
.build();
}
}
Response originalResponse = chain.proceed(request);
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=600")
.build();
}
}
then installing cache file is that simple
long SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(new File(context.getCacheDir(), "http"), SIZE_OF_CACHE);
OkHttpClient client = new OkHttpClient();
client.cache(cache);
client.networkInterceptors().add(new CachingControlInterceptor());

In your CachingControlInterceptor, you create new requests, but never actually use them. You call newBuilder and ignore the result, so the header modification is never actually sent any where. Try assigning those values to request and then instead of calling proceed on chain.request() call it on request.
public class CachingControlInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Add Cache Control only for GET methods
if (request.method().equals("GET")) {
if (ConnectivityUtil.checkConnectivity(getContext())) {
// 1 day
request = request.newBuilder()
.header("Cache-Control", "only-if-cached")
.build();
} else {
// 4 weeks stale
request = request.newBuilder()
.header("Cache-Control", "public, max-stale=2419200")
.build();
}
}
Response originalResponse = chain.proceed(request);
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=600")
.build();
}
}

you can also try:
public class CachingInterceptor implements Interceptor {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
request = new Request.Builder()
.cacheControl(new CacheControl.Builder()
.maxAge(1, TimeUnit.DAYS)
.minFresh(4, TimeUnit.HOURS)
.maxStale(8, TimeUnit.HOURS)
.build())
.url(request.url())
.build();
return chain.proceed(request);
}
}

I finally discovered the solution that worked for me in Retrofit 2.x and OkHttp 3.x
I had to implement two Interceptors, one of them is responsible to rewrite the Request headers and the other to rewrite the Response headers.
First, make sure you delete any old cache. (root explorer /data/data/com.yourapp/cache
Instantiate the client builder:
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(new RewriteRequestInterceptor())
.addNetworkInterceptor(new RewriteResponseCacheControlInterceptor())
Create the RewriteRequestInterceptor
public class RewriteRequestInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
int maxStale = 60 * 60 * 24 * 5;
Request request;
if (NetworkUtils.isNetworkAvailable()) {
request = chain.request();
} else {
request = chain.request().newBuilder().header("Cache-Control", "max-stale=" + maxStale).build();
}
return chain.proceed(request);
}
}
Create the RewriteResponseCacheControlInterceptor
public class RewriteResponseCacheControlInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
int maxStale = 60 * 60 * 24 * 5;
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder().header("Cache-Control", "public, max-age=120, max-stale=" + maxStale).build();
}
}
It's important to make sure you add the ResponseCacheControlInterceptor as a Network Interceptor, and the RewriteRequestInterceptor as a Interceptor (as I did in the 2nd step).

OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();

Related

Retrofit android header User-Agent not showing on Log

i do an interceptor and set header User-Agent like this
public class UserAgentInterceptor implements Interceptor {
private final String userAgent;
public UserAgentInterceptor(String userAgent) {
this.userAgent = userAgent;
}
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", userAgent)
.build();
return chain.proceed(requestWithUserAgent);
}
}
and i add log showen like this
private RestAdapter getRestAdapterFromUrl(String url)
{
OkHttpClient okHttp = new OkHttpClient();
okHttp.setConnectTimeout(18000, TimeUnit.MILLISECONDS);
okHttp.setReadTimeout(18000, TimeUnit.MILLISECONDS);
// On ajoute user agent intercepteur
okHttp.networkInterceptors().add(new UserAgentInterceptor(VersionUtil.getUserAgent()));
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(url)
.setClient(new RestoflashClientWrapper(new OkClient(okHttp)))
//.setConverter(new retrofit.converter.JacksonConverter(jsonMapperUnwrapped))
.setConverter(new JacksonConverter(jsonMapper,jsonMapperUnwrapped))
//.setRequestInterceptor(new RestoFlashRequest())
.setLogLevel(RestAdapter.LogLevel.FULL);
builder.setRequestInterceptor(interceptor);
builder.setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS).setLog(new AndroidLog("RETROFIT_LOG"));
return builder.build();
}
and also instead of RestAdapter.LogLevel.HEADERS_AND_ARGS . i tried RestAdapter.LogLevel.FULL but on the log i can' see the User-Agent header on the log of android studio ?
thanks you for helping
personally I had the same problem.
new OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
And i had to add an interceptor that explicitly log the headers.
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder();
/*log debug info*/
Request request = requestBuilder.build();
for (int i = 0; i < request.headers().size(); i++) {
Timber.tag("OkHttp").d(String.format("%s: %s", request.headers().name(i), request.headers().value(i)));
}
return chain.proceed(request);
}
And I also use a CurlInterceptor that allows to log a "curl", which is useful if you want to test quickly via tools such as "Postman".
CurlInterceptor sample here
Hoping to have answered your need!

Retrofit 2.x.x + Okhttp3 intercepter + Dagger +Rx - Connection not closed

I'm using
Retrofit 2.2.0 + okhttp3 Intercepter + GSONConverterFactory (Dagger2)+ RxJava2.
When checking on the server, it seem like the connection made by the app is kept alive, and not closed even after response is received.
So basically I have
App module - Where Retrofit with GSONCOnverterFactory and okhttp client and it's interceptor is present.
App Module class :
#Singleton
#Provides
protected MyService providesMyService(#Named("MyService") Retrofit retrofit) {
return retrofit.create(MyService.class);
}
#Singleton
#Provides
#Named("MyService")
protected Retrofit providesMyRetrofit(GsonConverterFactory factory) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(3000, TimeUnit.MILLISECONDS);
builder.readTimeout(3000, TimeUnit.MILLISECONDS);
builder.addInterceptor(new MyInterceptor());
builder.addNetworkInterceptor(new CachingControlInterceptor());
//caching
builder.cache(MyCache.getCache());
builder.addInterceptor(new HttpLoggingInterceptor()
.setLevel(loggingLevel));
try {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(builder.build())
.addConverterFactory(factory)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
} catch (Exception e) {
}
}
My Interceptor :
#Override
public Response intercept(Chain chain) throws IOException {
final Charset UTF8 = Charset.forName("UTF-8");
final String userPassword = USERNAME + ":" + PASSWORD;
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", SOME_AGENT)
.header("Authorization", "Basic " + new String(Base64.encodeBase64(userPassword.getBytes(UTF8)), UTF8))
.header("Content-Type", "application/json; charset=utf-8")
.header("Keep-Alive", "timeout = 3")
.method(original.method(), original.body())
.build();
Response response = chain.proceed(request);
return response;
}
this is Rx Code :
MyApp.getInstance().getAppComponent()
.getMyService()
.getData(queryMap, resultValues)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe((MyResponse myResponse) -> {
//success result handled here
}, throwable -> {
//exception result handled here
});
I don't see any warning in code or in logcat. Passing Keep-Alive : timeout in header is not helping too.
How can I verify from the app side if connection is closed properly, or if it is kept open even after the response is received on the app ?

How to change activity while get 401 status code in Interceptor Android

When I am getting 401 status code in API then I have to open login activity. I don't want to put change activity logic in every API's onError method. I want a global method which used for all the API's.
So for that, I created one Interceptor
public class MyInterceptor extends BaseActivity implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (response.code() == 401) {
throw new RuntimeException(" Here you got 401 from API !");
}
return response;
}
}
Here I add this Interceptor
OkHttpClient.Builder builder=new OkHttpClient.Builder()
.addNetworkInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("User-Agent", "MyApp-Android-App")
.build();
return chain.proceed(newRequest);
}
})
.addNetworkInterceptor(new StethoInterceptor())
.addNetworkInterceptor(new MyInterceptor())
.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
Now, where should I add change activity call method. One more thing I am calling API using RxRetrofit. I want a global method for handling this 401 response. Can you please provide me any solution where should I put Activity change method call?
create interceptor class
class HeaderInterceptor implements Interceptor
{
#Override
public Response intercept(Chain chain) throws IOException {
Request orignal = chain.request();
///--------------------------
String token="your auth token"; //retriving depend upon you
//---------------------------
Request request =orignal.newBuilder().header("Authorization", "Bearer "+ token)
.header("Accept", "application/json")
.header("Content-Type", "application/json").
method(orignal.method(), orignal.body())
.build();
Response response = chain.proceed(request);
Log.d("MyApp", "Code : "+response.code());
if (response.code() == 401){
//handle it as per ur need
return response;
}
return response;
}
}
now add interceptor
.....
.addInterceptor(logging).addInterceptor(new HeaderInterceptor())
.build();
..

Retrofit + OkHTTP - response cache not working

I know there has been a lot of similar questions, but I have read them all and none of them really helped.
So, here is my problem:
I am using retrofit + okhttp to fetch some data from API and I'd like to cache them. Unfortunately, I don't have admin access to the API server so I can't modify headers returned by the server. (currently, server returns Cache-control: private)
So I decided to use okhttp header spoofing to insert appropriate cache headers. Sadly, no matter what I do, caching doesn't seem to work.
I initialise the api service like this:
int cacheSize = 10 * 1024 * 1024; // 10 MiB
File cacheFile = new File(context.getCacheDir(), "thumbs");
final Cache cache = new Cache(cacheFile, cacheSize);
OkHttpClient client = new OkHttpClient();
client.setCache(cache);
client.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.removeHeader("Access-Control-Allow-Origin")
.removeHeader("Vary")
.removeHeader("Age")
.removeHeader("Via")
.removeHeader("C3-Request")
.removeHeader("C3-Domain")
.removeHeader("C3-Date")
.removeHeader("C3-Hostname")
.removeHeader("C3-Cache-Control")
.removeHeader("X-Varnish-back")
.removeHeader("X-Varnish")
.removeHeader("X-Cache")
.removeHeader("X-Cache-Hit")
.removeHeader("X-Varnish-front")
.removeHeader("Connection")
.removeHeader("Accept-Ranges")
.removeHeader("Transfer-Encoding")
.header("Cache-Control", "public, max-age=60")
//.header("Expires", "Mon, 27 Apr 2015 08:15:14 GMT")
.build();
}
});
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_ROOT)
.setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS)
.setClient(new OkClient(client))
.setConverter(new SimpleXMLConverter(false))
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
if (Network.isConnected(context)) {
int maxAge = 60; // read from cache for 2 minutes
request.addHeader("Cache-Control", "public, max-age=" + maxAge);
} else {
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
request.addHeader("Cache-Control",
"public, only-if-cached, max-stale=" + maxStale);
}
}
})
.build();
api = restAdapter.create(ApiService.class);
Of course, it's not necessary to remove all these headers, but I wanted to make the response as clean as possible to rule out some interference from these extra headers.
As you can see, I tried to also spoof Expires and Date header (I tried removing them, setting them so that there is exactly max-age differnece between them and also setting Expires far into future). I also experimented with various Cache-control values, but no luck.
I made sure the cacheFile exists, isDirectory and is writeable by the application.
These are the request and response headers as logged directly by retrofit:
Request:
Cache-Control: public, max-age=60
---> END HTTP (no body)
Response:
Date: Mon, 27 Apr 2015 08:41:10 GMT
Server: Apache/2.2.22 (Ubuntu)
Expires: Mon, 27 Apr 2015 08:46:10 GMT
Content-Type: text/xml; charset=UTF-8
OkHttp-Selected-Protocol: http/1.1
OkHttp-Sent-Millis: 1430124070000
OkHttp-Received-Millis: 1430124070040
Cache-Control: public, max-age=60
<--- END HTTP (-1-byte body)
<--- BODY: ...
And, finally one strange incident: At some point, the cache worked for a few minutes. I was getting reasonable hit counts, even offline requests returned cached values. (It happened while using the exact setting posted here) But when I restarted the app, everything was back to "normal" (constant hit count 0).
Co if anyone has any idea what could be the problem here, I'd be really glad for any help :)
Use networkInterceptors() instead of interceptors(). That in combination with your strategy of removing any headers that are somewhat related to caching will work. That's the short answer.
When you use interceptors to change headers it does not make any adjustments before CacheStrategy.isCacheable() is called. It's worthwhile to look at the CacheStrategy and CacheControl classes to see how OKHttp handles cache-related headers. It's also worthwhile to do ctrl+f "cache" on http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
I am not sure if the networkInterceptors() and interceptors() documentation is just unclear or if there is a bug. Once I look into that more, I will update this answer.
One more thing to add here, Apart from Brendan Weinstein's answer just to confirm OkHttp3 cache will not work with post requests.
After a full day, I found that my offline caching was not working just because I was using POST in the API type. The moment I changed it to GET, it worked!
#GET("/ws/audioInactive.php")
Call<List<GetAudioEntity>> getAudios();
My entire Retrofit class.
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.limnet.iatia.App;
import com.limnet.iatia.netio.entity.registration.APIInterfaceProviderIMPL;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RHTRetroClient {
public static final String BASE_URL = "https://abc.pro";
private static Retrofit retrofit = null;
private static RHTRetroClient mInstance;
private static final long cacheSize = 10 * 1024 * 1024; // 10 MB
public static final String HEADER_CACHE_CONTROL = "Cache-Control";
public static final String HEADER_PRAGMA = "Pragma";
private RHTRetroClient() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
Cache cache = new Cache(new File(App.getAppContext().getCacheDir(), "soundbites"),cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(httpLoggingInterceptor()) // used if network off OR on
.addNetworkInterceptor(networkInterceptor()) // only used when network is on
.addInterceptor(offlineInterceptor())
.build();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
/**
* This interceptor will be called both if the network is available and if the network is not available
*
* #return
*/
private static Interceptor offlineInterceptor() {
return new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Log.d("rht", "offline interceptor: called.");
Request request = chain.request();
// prevent caching when network is on. For that we use the "networkInterceptor"
if (!App.hasNetwork()) {
CacheControl cacheControl = new CacheControl.Builder()
.maxStale(7, TimeUnit.DAYS)
.build();
request = request.newBuilder()
.removeHeader(HEADER_PRAGMA)
.removeHeader(HEADER_CACHE_CONTROL)
.cacheControl(cacheControl)
.build();
}
return chain.proceed(request);
}
};
}
/**
* This interceptor will be called ONLY if the network is available
*
* #return
*/
private static Interceptor networkInterceptor() {
return new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Log.d("rht", "network interceptor: called.");
Response response = chain.proceed(chain.request());
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(5, TimeUnit.SECONDS)
.build();
return response.newBuilder()
.removeHeader(HEADER_PRAGMA)
.removeHeader(HEADER_CACHE_CONTROL)
.header(HEADER_CACHE_CONTROL, cacheControl.toString())
.build();
}
};
}
private static HttpLoggingInterceptor httpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor =
new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
Log.d("rht", "log: http log: " + message);
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return httpLoggingInterceptor;
}
public static synchronized RHTRetroClient getInstance() {
if (mInstance == null) {
mInstance = new RHTRetroClient();
}
return mInstance;
}
public APIInterfaceProviderIMPL getAPIInterfaceProvider() {
return retrofit.create(APIInterfaceProviderIMPL.class);
}
}
Check if there is a Pragma header in your response. Caching with max-age will not work if Pragma: no-cache header is present.
If it does have Pragma header, remove it by doing the following in your Interceptor:
override fun intercept(chain: Interceptor.Chain): Response {
val cacheControl = CacheControl.Builder()
.maxAge(1, TimeUnit.MINUTES)
.build()
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl.toString())
.removeHeader("Pragma") // Caching doesnt work if this header is not removed
.build()
}

How to specify a default user agent for okhttp 2.x requests

I am using okhttp 2.0 in my Android app and didn't find a way to set some common User Agent for all outgoing requests.
I thought I could do something like
OkHttpClient client = new OkHttpClient();
client.setDefaultUserAgent(...)
...but there's no such method or similar.
Of course I could provide some extension utility method which would wrap a RequestBuilder to attach .header("UserAgent") and then I would use it for building all my requests, but I thought maybe I missed some existing and simpler way?
You can use an interceptor to add the User-Agent header to all your requests.
For more information about okHttp interceptors see http://square.github.io/okhttp/interceptors/
Example implementation of this interceptor:
/* This interceptor adds a custom User-Agent. */
public class UserAgentInterceptor implements Interceptor {
private final String userAgent;
public UserAgentInterceptor(String userAgent) {
this.userAgent = userAgent;
}
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", userAgent)
.build();
return chain.proceed(requestWithUserAgent);
}
}
Test for the UserAgentInterceptor:
public void testUserAgentIsSetInRequestHeader() throws Exception {
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody("OK"));
server.play();
String url = server.getUrl("/").toString();
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new UserAgentInterceptor("foo/bar"));
Request testRequest = new Request.Builder().url(url).build()
String result = client.newCall(testRequest).execute().body().string();
assertEquals("OK", result);
RecordedRequest request = server.takeRequest();
assertEquals("foo/bar", request.getHeader("User-Agent"));
}
In case anyone is looking for this working with OkHttp 3 and in Kotlin:
val client = OkHttpClient.Builder()
.addNetworkInterceptor { chain ->
chain.proceed(
chain.request()
.newBuilder()
.header("User-Agent", "COOL APP 9000")
.build()
)
}
.build()
OkHttp v2.1 which is set to be released in the next few weeks will automatically set a User-Agent header if one is not already set.
As of now there isn't a good way to add this header to every request in a centralized way. The only workaround is to include the header manually for every Request that is created.
Based on #josketres answer, here is a similar Interceptor for OkHttp version 3
public class UserAgentInterceptor implements Interceptor {
private final String mUserAgent;
public UserAgentInterceptor(String userAgent) {
mUserAgent = userAgent;
}
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.header("User-Agent", mUserAgent)
.build();
return chain.proceed(request);
}
}
Plus the updated test:
#Test
public void testUserAgentIsSetInRequestHeader() throws IOException, InterruptedException {
final String expectedUserAgent = "foo/bar";
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody("OK"));
server.start();
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
okHttpBuilder.addInterceptor(new UserAgentInterceptor(expectedUserAgent));
Request request = new Request.Builder().url(server.url("/").url()).build();
ResponseBody result = okHttpBuilder.build().newCall(request).execute().body();
assertNotNull(result);
assertEquals("OK", result.string());
assertEquals(expectedUserAgent, server.takeRequest().getHeader("User-Agent"));
}
You have to use builder in newer versions. (Sep 2021)
client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#NotNull
#Override
public Response intercept(#NotNull Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", "My Agent is so cool")
.build();
return chain.proceed(requestWithUserAgent);
}
})
.build();
Using an intercepter is no longer required in the newer versions of OkHttp. Adding a user agent is as simple as:
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
Source: OkHttp wiki.

Categories

Resources