# OkHttp: pooled sockets, alternate IPs, and a client that refuses invalid requests

> OkHttp is a Kotlin HTTP client for the JVM, Android and GraalVM that treats the RFCs as a contract rather than a suggestion. That policy decision shapes what you can do with it, from a GET with a body you cannot send to a cache you cannot replace, and it leaves Maven users choosing between two artifacts because the okhttp one is empty.

**lysine-dev/okhttp** — GitHub describes it as A meticulous HTTP client for the JVM, Android, and GraalVM.. The repository metadata lists Kotlin as its primary language. The metadata lists the Apache-2.0 license. This article stays within the project description and details documented in the GitHub repository README.

- Repository: https://github.com/lysine-dev/okhttp
- Website: https://lysine.dev/okhttp/
- Stars: 47,080 · Forks: 9,294
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-08-13 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/lysine-dev-okhttp

## Four defaults do the work: HTTP/2 sharing, pooling, GZIP and the cache

The efficiency story is four defaults rather than a set of options. HTTP/2 support lets all requests to the same host share a socket. Connection pooling reduces request latency when HTTP/2 is not available. Transparent GZIP shrinks downloads. Response caching avoids the network entirely for repeat requests.

None of that is switched on by you, and that is the design position: the client is efficient by default, and the configuration surface is deliberately small.

The cost shows up in the one place you may want an escape hatch. One of the two stated limitations is that the cache is not an interface with alternative implementations. If you need a custom cache, an in-memory tier with a different eviction policy, or a cache you can inspect in a test, you cannot supply one; you use OkHttp's Cache or you wrap the client. For an application with an unusual caching requirement that is the boundary, and it is stated up front rather than discovered after design work.

## Spec conformance is a policy, and your workaround is the thing that gets refused

OkHttp names the specifications it follows: HTTP Semantics in RFC 9110, HTTP Caching in RFC 9111, HTTP/1.1 in RFC 9112, HTTP/2 in RFC 9113, Websockets in RFC 6455, and server-sent events. Where those documents are ambiguous, it follows modern user agents such as popular browsers or common HTTP libraries.

The part worth reading twice is the stance that follows. OkHttp is principled and avoids being overly configurable, particularly when a configuration exists to work around a buggy server, to test an invalid scenario, or to contradict the relevant RFC. Other HTTP libraries fill that gap, and they allow extensive customisation including potentially invalid requests.

For a reader, that is a budget line. If your service violates a specification, you will not find a flag in OkHttp that makes the violation work; you fix the service, or you move to a library that will send the malformed thing. The other stated limitation, that GET with a body is not allowed, is the same policy expressed as an outright refusal, and it removes a category of endpoint you can call from this client at all.

## When a connect fails, OkHttp tries the next address for that host

The behaviour that matters in production is recovery. If a service has multiple IP addresses, OkHttp attempts alternate addresses when the first connect fails, and the README names two situations where this is necessary: services reachable over both IPv4 and IPv6, and services hosted in redundant data centres.

There is also a fallback knob for connectivity, configurable to trade strictness for reach. That is the one place where broad compatibility is explicitly on the table, which is a useful contrast with the previous section: the client will bend on the network path, and it will not bend on the protocol.

The consequence for your code is about error handling rather than about success. A single logical request can involve more than one connect attempt against more than one address, so anything that treats a single connection failure as a single failed request, in metrics, in retry budgets, or in user-facing errors, will misreport. If your service is dual-stack, that path is normal traffic, not an edge case.

## The smallest useful program closes the response in a try-with-resources

The first example downloads a URL and returns its body as a string:

```java
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
```

The shape is the API: a Request built with a fluent builder, a client whose newCall returns a Call, and execute for the synchronous path. The response is consumed inside a try-with-resources, so the connection is returned to the pool when the block ends rather than when the object is collected. Reading the body as a string is the last thing you do with it.

The asynchronous path is the other half of the same builder, using callbacks, and the README states that both synchronous blocking calls and async calls with callbacks are supported. For a service codebase the decision is per call site, not per application, which is worth knowing before you wrap a client in your own facade and lose the option.

## A POST needs a MediaType, and a Maven build gets an empty okhttp artifact

Posting is the same shape with a body and a declared media type:

```java
public static final MediaType JSON = MediaType.get("application/json");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(json, JSON);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
```

Now the packaging trap, which only affects one build system. OkHttp is published as a Kotlin Multiplatform project. Gradle handles the platform selection automatically, but Maven projects must choose between okhttp-jvm and okhttp-android, and the okhttp artifact will be empty in a Maven project. A Maven build that declares okhttp gets a dependency that resolves and does nothing, which is the kind of failure that shows up as a missing class rather than as a build error.

If you are on Maven, the BOM is what keeps the pieces aligned:

```xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp-bom</artifactId>
      <version>5.2.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
```

Note the version drift inside the project's own sample: the BOM is 5.2.0 while the artifacts it manages are declared separately, and one of them carries a comment to remove it after OkHttp 5.2.0 ships with an updated BOM. Read the sample as a template to fix, not a file to paste.

## Gradle users get one coordinate, and 5.5.0 is the current one

On Gradle the whole dependency is a line:

```kotlin
implementation("com.squareup.okhttp3:okhttp:5.5.0")
```

5.5.0 is the version the README names as the latest release, and it is also the version of the bill of materials, so aligning artifacts is a second line rather than a habit:

```kotlin
    dependencies {
       // define a BOM and its version
       implementation(platform("com.squareup.okhttp3:okhttp-bom:5.5.0"))

       // define any required OkHttp artifacts without version
       implementation("com.squareup.okhttp3:okhttp")
       implementation("com.squareup.okhttp3:logging-interceptor")
    }
```

The floor matters as much as the version. OkHttp works on Android 5.0 and later, API level 21 and later, and Java 8 and later. A 3.12.x branch exists for Android 2.3 and Java 7, and the README says plainly that those platforms lack TLS 1.2 support and should not be used. So if your build has a legacy minSdk or a Java 7 target, the old branch is the only thing that resolves, and the project is telling you it is the wrong answer. R8 and ProGuard rules and snapshot builds are also published, which matters if you shrink an Android release build.

## Android: disable the startup initializer and you own the initialization call

On Android, OkHttp uses AndroidX Startup, which means an initializer is registered for you. The failure mode is defined precisely: if you disable the initializer in the manifest, then your app is responsible for calling OkHttp.initialize(applicationContext) in Application.onCreate.

That is a small sentence with a large consequence. Manifest merging and proguard rules routinely strip initializers that an app does not know about, and the resulting error appears far from the manifest, at the first call, in code that never mentions initialization. Any team that removes the initializer for startup-time reasons has to add the Application.onCreate call in the same change.

The rest of the surface is mobile-shaped rather than novel: the repository carries android-test/ and android-test-app/ alongside a dedicated samples/android/ project, and the samples tree as a whole includes simple-client, guide, crawler, compare, slack, static-server, tlssurvey and unixdomainsockets. Pick the sample that matches the transport you are testing; unixdomainsockets and tlssurvey exist because those are the cases that a general-purpose HTTP tutorial leaves out.

## MockWebServer is the answer for basic tests and the wrong answer for the next ones

OkHttp ships a testing library for HTTP, HTTPS and HTTP/2 clients, added like any other dependency:

```kotlin
testImplementation("com.squareup.okhttp3:mockwebserver3:5.5.0")
```

The README is unusually direct about its scope. MockWebServer is used firstly for internal testing, and for basic testing of apps that use the OkHttp client. It is not a full-featured HTTP testing library developed standalone, and it is not being actively developed for new features. The advice that follows is that your needs will outgrow it and you may want a more full-featured testing library.

Plan for that rather than discovering it. If your tests need protocol-level scripting, several servers at once, or fault injection beyond a canned response, the honest answer from this project is that you will be leaving. The tree shows the churn behind that statement, with mockwebserver/, mockwebserver-junit4/, mockwebserver-junit5/ and a mockwebserver-deprecated/ directory side by side. Separately, OkHttp itself is not archived and its last push was 2026-09-25, so the client is moving while its test server is not gaining features.

## Conclusion

OkHttp fits a service or Android codebase that wants spec-conformant behaviour, TLS from the platform and no configuration switches for working around a broken server. It does not fit a team that needs to send a GET with a body, swap the cache implementation, or rely on MockWebServer as a growing protocol-testing suite, since the README states each of those gaps. Verify first by adding the Gradle dependency at 5.5.0, and if you are on Maven, confirm you declared okhttp-jvm or okhttp-android rather than the empty okhttp artifact.

## FAQ

### What is OkHttp?

OkHttp is an HTTP client for the JVM, Android and GraalVM, written in Kotlin and published as a Kotlin Multiplatform project. It shares sockets across requests to the same host over HTTP/2, pools connections, applies GZIP transparently and caches responses.

### Is OkHttp blocking?

Both paths exist. The request and response API is built with fluent builders and immutability, and supports synchronous blocking calls as well as async calls with callbacks, which the examples show through Call.execute().

### How do I use OkHttp in Java?

Build a Request with Request.Builder, create it from a shared OkHttpClient with newCall, and call execute() for the synchronous form, as in the download and post examples. Responses are consumed inside a try-with-resources, and the client is meant to be shared rather than created per request.

### How do I use OkHttp in Android?

OkHttp works on Android 5.0 and later, API level 21 and later, and uses AndroidX Startup by default. If you disable the initializer in the manifest, your app must call OkHttp.initialize(applicationContext) in Application.onCreate.

### How do I add the OkHttp library in Android Studio?

With Gradle, add the single coordinate implementation("com.squareup.okhttp3:okhttp:5.5.0"), which is the version the README names as the latest release. A bill of materials is also published so multiple OkHttp artifacts can be declared without versions.

## Sources

- [Official documentation](https://lysine.dev/okhttp/)
- [Official README](https://github.com/lysine-dev/okhttp#readme)
- [Project repository](https://github.com/lysine-dev/okhttp)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lysine-dev-okhttp
