Recently, a discussion arose that our app startup time felt slow. Our first guess was that this was due to the “big” /bootstrap
endpoint that we call on every app start. However, someone mentioned that our app uses ETags
. Without knowing the technical details about that, we know that matching ETags
should omit the content body of the HTTP response, thus speeding up HTTP responses. And the /bootstrap
content is not supposed to change that often.
Since everything was a bit fuzzy, we decided to dig deeper into this topic to investigate in it. Luckily, I was the one in charge.
In this blog, I wanted to share my findings not only with my colleagues but with the world.
Basics
Response
Servers may include the ETag
header in the response. The ETag
value can be any valid ASCII character. However, it is typically a hash of the (response) content.
There is also a special syntax for weak ETags
. If an ETag
is determined to be weak, then the header value should be prefixed with W/
.
Examples:
# Stable ETag
ETag: "0123456789"
# Weak ETag
Etag: W/"0123456789"
A weak ETag
is described as having the same semantic content, but it is not byte identical. Nevertheless, weak ETags
can be used for caching purposes.
Check out the MDN website for more.
Request
If clients receive a header with an ETag
, and they have the ability to cache the content, subsequent requests on the same endpoint can include an If-Non-Match
header with the ETag
value as the value.
The server can validate the content and, if nothing has changed, return a 304 Not Modified
. The content will be omitted from the response.
Example:
curl -I -H "If-None-Match: W/\"b710206e3486741a412bc5c23df17f80\"" https://[SomeWhere.com]/api/passenger/user
In this case, the ETag
value is W/”b710206e3486741a412bc5c23df17f80”
. If this value matches with the previous ETag
header value you received from the server, and the content on that endpoint hasn’t changed, the response from this curl command should show you a 304
(status code).
OkHttp
In our Android app, we use OkHttp to make HTTP calls. In debug mode we log every single HTTP call to logcat, using the HttpLoggingInterceptor
.
OkHttp has ETag
support out of the box. You just have to enable caching by calling client.cache()
. The library will do the rest for you.
While inspecting various API calls, I noticed that the server always responds with a 200 OK
. I was expecting a 304
because no one has actually changed this user in the meantime.
My second attempt was to use the Android Studio Network Inspector. And indeed, here I noticed that all the requests returned a 304
.
After some research, I found out that there are two types of Interceptors
in OkHttp. The application interceptors and the network interceptors. Check out the product website to see their differences in detail.
However, we have added the HttpLoggingInterceptor
as an application interceptor. This one omits the output of ETags
. It is even mentioned in the docs:
Observe the application’s original intent. Unconcerned with OkHttp-injected headers like
If-None-Match
.
Adding the HttpLoggingInterceptor
to the network interceptors would also show the correct response code in logcat.
Note: This is not a general call to add the
HttpLoggingInterceptor
to the network interceptors❗️It was just an observation that I wanted to share with you.
Still here? Great. Thanks for reading!
If you’re curious if the slow app startup time was caused by the /bootstrap
endpoint, I can tell you: No, it wasn’t. Even such a “big JSON” (~110 KB) takes “only” 250ms - 500ms on an emulator running on a 120MB/s WLAN-connected laptop. Sure, it is not fast, but it is definitely not slow.
Since this is not the cause of the problem, we may have to look into other issues a bit more 😉