OkHttp & Interceptors Quiz
DATA › Networking
Which interceptor type is guaranteed to be invoked exactly once per call, even when OkHttp follows redirects or serves the response from cache?
- Application interceptor (addInterceptor)
- Network interceptor (addNetworkInterceptor)
- Both run exactly once per call
- Neither; both run once per network hop
Answer: Application interceptor (addInterceptor)
Application interceptors run once per logical call regardless of redirects, retries, or cache hits. Network interceptors run once per actual network request, so they can fire multiple times or not at all (cache hit).
You add a network interceptor that logs Accept-Encoding. Why might it show 'gzip' even though your code never set that header?
- Network interceptors corrupt headers, so gzip can appear there
- OkHttp adds Accept-Encoding: gzip, and network interceptors see it
- Application interceptors always insert gzip before the request goes out
- The server echoes Accept-Encoding back into the request headers
Answer: OkHttp adds Accept-Encoding: gzip, and network interceptors see it
When you do not set Accept-Encoding yourself, OkHttp adds gzip transparently. Network interceptors observe the request after OkHttp's own headers are added, so they see it; application interceptors do not.
What is the correct way to add an Authorization header to every outgoing request in OkHttp?
- Mutate chain.request() in place and call chain.proceed()
- Subclass OkHttpClient and override its request-building method
- Copy via newBuilder().header(...) and call chain.proceed()
- Set the header directly on the shared ConnectionPool
Answer: Copy via newBuilder().header(...) and call chain.proceed()
Request is immutable, so you create a modified copy with newBuilder() and proceed with it. There is no in-place mutation and no client subclassing involved.
Which configuration is the appropriate place to enforce certificate pinning, and on which client object?
- A custom TrustManager applied per request in an interceptor
- A CertificatePinner set on OkHttpClient.Builder
- A header inserted by a network interceptor during the request
- The Cache on OkHttpClient used to store trusted certs
Answer: A CertificatePinner set on OkHttpClient.Builder
Pinning is configured with CertificatePinner on OkHttpClient.Builder().certificatePinner(...). It validates the server's certificate chain against expected SHA-256 public-key hashes during the TLS handshake.
Your app shares a single OkHttpClient instance across the whole process. What is the main benefit?
- It disables all interceptors to improve performance
- It forces every single request to go through the cache
- It reuses the connection pool, thread pools, and cache
- It guarantees that requests always run on the main thread
Answer: It reuses the connection pool, thread pools, and cache
OkHttpClient holds the ConnectionPool, dispatcher thread pools, and cache. Sharing one instance lets keep-alive connections and threads be reused instead of recreated per request.
callTimeout is set to 0 (the default) while connectTimeout, readTimeout, and writeTimeout are each 10 seconds. What does this mean for a request that follows two redirects?
- The entire call, including both redirects, must complete within 10 seconds.
- No total cap; each connect/read/write phase on each hop still has 10s.
- The request fails at once because setting callTimeout to 0 is invalid.
- Redirects are blocked whenever callTimeout is 0, even if per-phase limits apply.
Answer: No total cap; each connect/read/write phase on each hop still has 10s.
callTimeout = 0 means no limit on total call duration (including redirects, retries, and body). The per-phase connect/read/write timeouts still apply independently to each network operation.
Which statement about OkHttp's application interceptors is FALSE?
- They can short-circuit a call by returning a response without calling chain.proceed()
- They can retry by calling chain.proceed() multiple times
- They have direct access to the underlying Connection for TLS/IP inspection
- They see the response after redirects are resolved, as a single logical result
Answer: They have direct access to the underlying Connection for TLS/IP inspection
Access to the Connection (chain.connection(), for IP/TLS inspection) is available to network interceptors, not application interceptors. The other three statements are true of application interceptors.
Your API returns 401 when an access token has expired, and you want OkHttp to transparently obtain a fresh token and retry the original request. Which mechanism is purpose-built for this?
- OkHttp's Authenticator via authenticator(), which retries on 401
- A network interceptor that watches responses and re-issues the call on 401
- A CertificatePinner wired with a token-refresh callback for expired access
- The Cache, by marking the request onlyIfCached to force a fresh token
Answer: OkHttp's Authenticator via authenticator(), which retries on 401
Authenticator is called reactively after the server responds 401 (or 407 for proxies); it returns a new Request to retry or null to give up, and inspecting the prior Authorization header lets you avoid infinite refresh loops.
You register addInterceptor(A) and then addInterceptor(B) on the same client. In what order do they process an outgoing request and its response?
- B processes the request first, then A, since order reverses on each hop.
- A sees the request before B, and B sees the response before A.
- The order is undefined and chosen by Dispatcher each time at runtime.
- Both run concurrently on separate dispatcher threads during the request.
Answer: A sees the request before B, and B sees the response before A.
Interceptors form an ordered chain in registration order: the request flows A then B outward via chain.proceed(), and the response unwinds B then A. Application interceptors always sit above the network interceptors and the network code.
Inside an application interceptor you call response.body().string() to inspect the payload, then return that same response object to the caller. What happens at the call site?
- The caller reads it normally because response bodies are reusable
- OkHttp buffers it again automatically before returning the response
- The connection is cached, and the body is transparently re-streamed
- The caller gets an empty or closed body; the stream was consumed
Answer: The caller gets an empty or closed body; the stream was consumed
A ResponseBody is a one-shot stream that string() fully consumes and closes. To inspect and still forward it, use peekBody() or rebuild the Response with a fresh ResponseBody created from the bytes you read.
Why should HttpLoggingInterceptor.Level.BODY not be left enabled in a release build?
- It logs full headers and bodies to Logcat, leaking tokens and PII
- It rewrites request bytes, breaking server-side signatures
- It forces every request to go through the response cache
- It silently disables HTTP/2 and downgrades connections to HTTP/1.1
Answer: It logs full headers and bodies to Logcat, leaking tokens and PII
BODY level writes headers and full bodies to Logcat, exposing bearer tokens and user data on a device's logs. Restrict it to debug builds and call redactHeader() for any sensitive header you must keep.
With a Cache configured, you want stored responses served when the device is offline even though the server's Cache-Control marks them stale. What is the idiomatic OkHttp approach?
- Set callTimeout to 0 so the call waits for connectivity to return
- Add a network interceptor that strips out the response body bytes
- Add an interceptor setting Cache-Control: max-stale when offline
- Enable CertificatePinner so the cached certificates get reused
Answer: Add an interceptor setting Cache-Control: max-stale when offline
An interceptor can rewrite the request Cache-Control (e.g. CacheControl.FORCE_CACHE or only-if-cached with max-stale) when offline, so OkHttp returns a stored response instead of failing; online you let normal validation proceed.
OkHttp's Dispatcher caps how many asynchronous (enqueue) calls run at once. Which settings control that concurrency?
- connectTimeout and readTimeout on the Builder settings
- The ConnectionPool's max idle connection count for reuse
- The Cache's maxSize in bytes for stored response bodies
- Dispatcher.maxRequests and Dispatcher.maxRequestsPerHost
Answer: Dispatcher.maxRequests and Dispatcher.maxRequestsPerHost
maxRequests (default 64) caps total in-flight async calls and maxRequestsPerHost (default 5) caps per-host concurrency. The ConnectionPool governs idle keep-alive connections, which is a separate concern from dispatch concurrency.
OkHttp transparently decompresses gzipped responses, but to send a gzip-compressed request body you must do what?
- Set Accept-Encoding: gzip on the request and let the server compress it
- Nothing, because OkHttp compresses request bodies automatically for you
- Add an interceptor that sets Content-Encoding: gzip and gzips the body
- Call OkHttpClient.Builder().gzipRequests(true) to enable body compression
Answer: Add an interceptor that sets Content-Encoding: gzip and gzips the body
Request-body compression is never automatic; you add a Content-Encoding: gzip header and wrap the RequestBody to gzip its bytes (typically with Okio's GzipSink). Transparent gzip in OkHttp applies only to responses.