0
# Interceptors
1
2
Request and response interception for logging, authentication, caching, and custom processing.
3
4
## Capabilities
5
6
### Interceptor
7
8
Observes and modifies HTTP requests/responses in processing chain.
9
10
```java { .api }
11
public interface Interceptor {
12
Response intercept(Chain chain) throws IOException;
13
14
interface Chain {
15
Request request();
16
Response proceed(Request request) throws IOException;
17
Connection connection();
18
}
19
}
20
```
21
22
### Chain Interface
23
24
Provides access to the request and allows proceeding with processing.
25
26
```java { .api }
27
interface Chain {
28
/**
29
* Returns the request being processed.
30
* @return the Request object
31
*/
32
Request request();
33
34
/**
35
* Proceeds with the request processing, possibly after modification.
36
* @param request the request to process (original or modified)
37
* @return the Response from processing
38
* @throws IOException if processing fails
39
*/
40
Response proceed(Request request) throws IOException;
41
42
/**
43
* Returns the connection that will carry this request, or null if unknown.
44
* @return the Connection or null
45
*/
46
Connection connection();
47
}
48
```
49
50
**Basic Usage Examples:**
51
52
```java
53
// Logging interceptor
54
class LoggingInterceptor implements Interceptor {
55
@Override
56
public Response intercept(Chain chain) throws IOException {
57
Request request = chain.request();
58
59
long startTime = System.nanoTime();
60
System.out.println("Sending request " + request.url());
61
62
Response response = chain.proceed(request);
63
64
long endTime = System.nanoTime();
65
System.out.println("Received response for " + response.request().url() +
66
" in " + (endTime - startTime) / 1e6d + "ms");
67
68
return response;
69
}
70
}
71
72
// Add to client
73
client.interceptors().add(new LoggingInterceptor());
74
75
// Authentication interceptor
76
client.interceptors().add(new Interceptor() {
77
@Override
78
public Response intercept(Chain chain) throws IOException {
79
Request original = chain.request();
80
Request.Builder requestBuilder = original.newBuilder()
81
.header("Authorization", "Bearer " + getAccessToken());
82
Request request = requestBuilder.build();
83
return chain.proceed(request);
84
}
85
});
86
```
87
88
### Application vs Network Interceptors
89
90
OkHttp supports two types of interceptors with different capabilities and timing.
91
92
```java { .api }
93
/**
94
* Application interceptors observe the full span of each call.
95
* They see the original request and final response.
96
*/
97
public List<Interceptor> interceptors();
98
99
/**
100
* Network interceptors observe each network request and response.
101
* They see the actual network requests including redirects and retries.
102
*/
103
public List<Interceptor> networkInterceptors();
104
```
105
106
**Usage Examples:**
107
108
```java
109
// Application interceptor - sees original request
110
client.interceptors().add(new Interceptor() {
111
@Override
112
public Response intercept(Chain chain) throws IOException {
113
Request request = chain.request();
114
System.out.println("App interceptor: " + request.url());
115
return chain.proceed(request);
116
}
117
});
118
119
// Network interceptor - sees actual network requests
120
client.networkInterceptors().add(new Interceptor() {
121
@Override
122
public Response intercept(Chain chain) throws IOException {
123
Request request = chain.request();
124
System.out.println("Network interceptor: " + request.url());
125
// This may be called multiple times for redirects
126
return chain.proceed(request);
127
}
128
});
129
```
130
131
### Authentication Interceptors
132
133
Implement various authentication schemes using interceptors.
134
135
**Usage Examples:**
136
137
```java
138
// Bearer token authentication
139
class BearerTokenInterceptor implements Interceptor {
140
private final String token;
141
142
public BearerTokenInterceptor(String token) {
143
this.token = token;
144
}
145
146
@Override
147
public Response intercept(Chain chain) throws IOException {
148
Request original = chain.request();
149
Request authenticated = original.newBuilder()
150
.header("Authorization", "Bearer " + token)
151
.build();
152
return chain.proceed(authenticated);
153
}
154
}
155
156
// API key authentication
157
class ApiKeyInterceptor implements Interceptor {
158
private final String apiKey;
159
160
public ApiKeyInterceptor(String apiKey) {
161
this.apiKey = apiKey;
162
}
163
164
@Override
165
public Response intercept(Chain chain) throws IOException {
166
Request original = chain.request();
167
HttpUrl url = original.httpUrl().newBuilder()
168
.addQueryParameter("api_key", apiKey)
169
.build();
170
Request request = original.newBuilder()
171
.url(url)
172
.build();
173
return chain.proceed(request);
174
}
175
}
176
177
// Basic authentication with retry on 401
178
class BasicAuthInterceptor implements Interceptor {
179
private final String username;
180
private final String password;
181
182
public BasicAuthInterceptor(String username, String password) {
183
this.username = username;
184
this.password = password;
185
}
186
187
@Override
188
public Response intercept(Chain chain) throws IOException {
189
Request original = chain.request();
190
191
// Try request without auth first
192
Response response = chain.proceed(original);
193
194
// If 401, add auth and retry
195
if (response.code() == 401) {
196
response.body().close();
197
String credentials = Credentials.basic(username, password);
198
Request authenticated = original.newBuilder()
199
.header("Authorization", credentials)
200
.build();
201
return chain.proceed(authenticated);
202
}
203
204
return response;
205
}
206
}
207
```
208
209
### Request Modification Interceptors
210
211
Modify requests before they are sent to the server.
212
213
**Usage Examples:**
214
215
```java
216
// Add common headers
217
class HeaderInterceptor implements Interceptor {
218
@Override
219
public Response intercept(Chain chain) throws IOException {
220
Request original = chain.request();
221
Request modified = original.newBuilder()
222
.header("User-Agent", "MyApp/1.0")
223
.header("Accept", "application/json")
224
.header("X-Client-Version", "2.1.0")
225
.build();
226
return chain.proceed(modified);
227
}
228
}
229
230
// URL rewriting
231
class UrlRewriteInterceptor implements Interceptor {
232
@Override
233
public Response intercept(Chain chain) throws IOException {
234
Request original = chain.request();
235
236
// Rewrite localhost to staging server
237
HttpUrl originalUrl = original.httpUrl();
238
HttpUrl newUrl = originalUrl;
239
240
if ("localhost".equals(originalUrl.host())) {
241
newUrl = originalUrl.newBuilder()
242
.host("staging.api.example.com")
243
.build();
244
}
245
246
Request modified = original.newBuilder()
247
.url(newUrl)
248
.build();
249
return chain.proceed(modified);
250
}
251
}
252
```
253
254
### Response Processing Interceptors
255
256
Process and modify responses before they reach the application.
257
258
**Usage Examples:**
259
260
```java
261
// Response caching headers
262
class CacheInterceptor implements Interceptor {
263
@Override
264
public Response intercept(Chain chain) throws IOException {
265
Response original = chain.proceed(chain.request());
266
267
// Force cache for GET requests
268
if ("GET".equals(chain.request().method())) {
269
return original.newBuilder()
270
.header("Cache-Control", "max-age=300")
271
.build();
272
}
273
274
return original;
275
}
276
}
277
278
// Error response logging
279
class ErrorLoggingInterceptor implements Interceptor {
280
@Override
281
public Response intercept(Chain chain) throws IOException {
282
Request request = chain.request();
283
Response response = chain.proceed(request);
284
285
if (!response.isSuccessful()) {
286
System.err.printf("HTTP %d for %s: %s%n",
287
response.code(),
288
request.url(),
289
response.message());
290
291
// Log response body for debugging (careful with large responses)
292
if (response.body().contentLength() < 1024) {
293
String bodyString = response.body().string();
294
System.err.println("Error body: " + bodyString);
295
296
// Create new response with same body content
297
response = response.newBuilder()
298
.body(ResponseBody.create(response.body().contentType(), bodyString))
299
.build();
300
}
301
}
302
303
return response;
304
}
305
}
306
```
307
308
### Retry and Circuit Breaker Interceptors
309
310
Implement resilience patterns using interceptors.
311
312
**Usage Examples:**
313
314
```java
315
// Simple retry interceptor
316
class RetryInterceptor implements Interceptor {
317
private final int maxRetries;
318
319
public RetryInterceptor(int maxRetries) {
320
this.maxRetries = maxRetries;
321
}
322
323
@Override
324
public Response intercept(Chain chain) throws IOException {
325
Request request = chain.request();
326
Response response = null;
327
IOException lastException = null;
328
329
for (int attempt = 0; attempt <= maxRetries; attempt++) {
330
try {
331
if (response != null) {
332
response.body().close();
333
}
334
response = chain.proceed(request);
335
if (response.isSuccessful() || !isRetryable(response.code())) {
336
break;
337
}
338
} catch (IOException e) {
339
lastException = e;
340
if (attempt == maxRetries) {
341
throw e;
342
}
343
}
344
345
// Wait before retry
346
try {
347
Thread.sleep(1000 * (attempt + 1)); // Exponential backoff
348
} catch (InterruptedException ie) {
349
Thread.currentThread().interrupt();
350
throw new IOException("Interrupted during retry", ie);
351
}
352
}
353
354
if (response == null && lastException != null) {
355
throw lastException;
356
}
357
358
return response;
359
}
360
361
private boolean isRetryable(int code) {
362
return code >= 500 || code == 408 || code == 429;
363
}
364
}
365
366
// Rate limiting interceptor
367
class RateLimitInterceptor implements Interceptor {
368
private final long minIntervalMs;
369
private volatile long lastRequestTime = 0;
370
371
public RateLimitInterceptor(long minIntervalMs) {
372
this.minIntervalMs = minIntervalMs;
373
}
374
375
@Override
376
public Response intercept(Chain chain) throws IOException {
377
synchronized (this) {
378
long now = System.currentTimeMillis();
379
long timeSinceLastRequest = now - lastRequestTime;
380
381
if (timeSinceLastRequest < minIntervalMs) {
382
try {
383
Thread.sleep(minIntervalMs - timeSinceLastRequest);
384
} catch (InterruptedException e) {
385
Thread.currentThread().interrupt();
386
throw new IOException("Interrupted during rate limiting", e);
387
}
388
}
389
390
lastRequestTime = System.currentTimeMillis();
391
}
392
393
return chain.proceed(chain.request());
394
}
395
}
396
```
397
398
### Debugging and Monitoring Interceptors
399
400
Interceptors for debugging, monitoring, and performance analysis.
401
402
**Usage Examples:**
403
404
```java
405
// Performance monitoring interceptor
406
class PerformanceInterceptor implements Interceptor {
407
@Override
408
public Response intercept(Chain chain) throws IOException {
409
Request request = chain.request();
410
long startTime = System.nanoTime();
411
412
Response response = chain.proceed(request);
413
414
long duration = System.nanoTime() - startTime;
415
long durationMs = duration / 1_000_000;
416
417
// Log slow requests
418
if (durationMs > 1000) {
419
System.out.printf("SLOW REQUEST: %s took %dms%n", request.url(), durationMs);
420
}
421
422
// Add timing header to response
423
return response.newBuilder()
424
.header("X-Response-Time", durationMs + "ms")
425
.build();
426
}
427
}
428
```