SReverseby Simpa Labs

OkHttp internals

Finding OkHttp network logic inside an Android application

OkHttp is the HTTP layer under most Android networking libraries, including Retrofit. Request behaviour that looks absent from the interface declarations usually lives in the client configuration and the interceptor chain.

The pipeline a call travels through

An OkHttp call is assembled from a client, a request and a dispatcher. The client holds shared configuration: timeouts, the connection pool, the cache, the cookie jar, the dispatcher and the lists of interceptors. A request holds the method, URL, headers and body for one call. The dispatcher queues asynchronous calls and enforces the concurrency limits the client was built with.

Understanding the order matters, and memorising class names does not. Application interceptors run before the connection is established and can see every redirect and retry as separate invocations. Network interceptors run once per network attempt and are the right place to observe what actually goes on the wire. The order in which interceptors were added is the order they execute, and a signing interceptor placed at the end of the chain sees a different request than one placed at the beginning.

Find the client construction

Start where the object graph is assembled. Many apps build a single client in the application class and share it. Others hand construction to a dependency injection module that provides the client as a singleton, in which case the module is the file to read. A few build a client per feature, which is worth knowing because the interceptor sets will differ.

The builder call sequence is the most compact record of how the app talks to its server. Each method call on the builder is a decision: a timeout value, a retry policy, a certificate pinner, a proxy selection, an authenticator that reacts to an authentication challenge. Read it in order and you have the transport API specification.

Read the interceptor list as a list of behaviours

Interceptors are where the app adds what the API description omits. Common occupants of the list:

  • A logging interceptor, which is convenient for you because it already prints the final request unless it is configured to redact headers.
  • An authentication interceptor that attaches a token and refreshes it when a response indicates expiry.
  • A signing interceptor that computes a value over the method, path, body or a subset of headers. This one is usually last in the application chain.
  • A header interceptor that adds device, locale, version or tenant identifiers.
  • A rewriting interceptor that changes the host for a staging environment or pins traffic to a particular endpoint.

Each of these runs on every call, which is why a capture can contain headers that appear nowhere in an interface declaration and can contain a value that changes on each send.

Trace a request from the interface to the socket

Retrofit does not bypass OkHttp. It parses the annotations, builds an OkHttp request and hands it to the client. If you have the interface list, the client construction connects it to the transport, and the interceptor chain explains the delta between the two.

When you need the bytes as sent rather than as built, observe after the last application interceptor. Hooking the network layer, or reading the logging interceptor output at the correct position, gives you the request after mutations. Logging the request as passed into the first interceptor gives you the pre-signing version, which is the wrong artifact to compare against a capture.

Where configuration hides outside the client

Values that the chain consumes are often produced elsewhere. A signing key may be fetched during startup and held in memory. A device identifier may be computed from build properties or backed by the keystore. A cookie jar may persist state between runs, so a call that works after a fresh install fails after a restart. Reading only the interceptor shows the transformation without showing the source of its inputs.

Native code is the other hiding place. An interceptor may call a method declared in a loaded library, in which case the Java side shows the shape of the input while the computation happens in compiled C or C++. That boundary is visible in the interceptor as a native method declaration or a call into a wrapper class, and it changes the tooling you need.

Confirm behaviour by changing one input

Once you can see the chain, test your reading of it. Disable or stub one interceptor and observe which requests change. Modify a single header that you believe is signed and watch whether the server rejects the call. Compare a request from a cold start with one sent later in the session, which exposes token rotation and cached configuration. Each experiment either confirms the chain or points at a piece you have not located.

This is the stage where interception stops being an academic exercise. A team that can read the chain can reproduce it. Reproducing it at scale, with retries, cookie persistence and error paths, is what the OkHttp interceptor work delivers, and a Python client is the usual way to run it on a schedule.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?