0
# Advanced Features
1
2
Advanced MockWebServer capabilities including custom dispatchers for dynamic response logic, HTTP/2 server push promises, duplex streaming, SSL/TLS configuration, protocol negotiation, and WebSocket upgrades.
3
4
## Capabilities
5
6
### Custom Dispatchers
7
8
Create dynamic response logic by extending the Dispatcher class or using QueueDispatcher with advanced configurations.
9
10
```kotlin { .api }
11
/**
12
* Base class for handling mock server requests with custom logic
13
*/
14
abstract class Dispatcher {
15
/**
16
* Process incoming request and return appropriate response
17
* @param request RecordedRequest containing request details
18
* @returns MockResponse to send back to client
19
*/
20
abstract fun dispatch(request: RecordedRequest): MockResponse
21
22
/**
23
* Get preview of next response without consuming it
24
* @returns MockResponse that would be returned by next dispatch call
25
*/
26
open fun peek(): MockResponse
27
28
/**
29
* Release any resources held by the dispatcher
30
*/
31
open fun shutdown()
32
}
33
34
/**
35
* Default dispatcher that serves responses from a FIFO queue
36
*/
37
class QueueDispatcher : Dispatcher() {
38
/**
39
* Add response to end of queue
40
* @param response MockResponse to enqueue
41
*/
42
open fun enqueueResponse(response: MockResponse)
43
44
/**
45
* Enable/disable fail-fast mode for empty queue
46
* @param failFast If true, return error response when queue is empty
47
*/
48
open fun setFailFast(failFast: Boolean)
49
50
/**
51
* Set specific response for fail-fast mode
52
* @param failFastResponse Response to return when queue is empty (null for default 404)
53
*/
54
open fun setFailFast(failFastResponse: MockResponse?)
55
}
56
```
57
58
**Usage Examples:**
59
60
```kotlin
61
// Custom dispatcher for RESTful API simulation
62
class ApiDispatcher : Dispatcher() {
63
private val users = mutableMapOf(
64
1 to """{"id": 1, "name": "Alice"}""",
65
2 to """{"id": 2, "name": "Bob"}"""
66
)
67
68
override fun dispatch(request: RecordedRequest): MockResponse {
69
val path = request.path ?: return MockResponse().setResponseCode(400)
70
71
return when {
72
path.matches(Regex("/api/users/\\d+")) && request.method == "GET" -> {
73
val userId = path.substringAfterLast("/").toIntOrNull()
74
val user = users[userId]
75
if (user != null) {
76
MockResponse()
77
.setResponseCode(200)
78
.addHeader("Content-Type", "application/json")
79
.setBody(user)
80
} else {
81
MockResponse().setResponseCode(404)
82
}
83
}
84
85
path == "/api/users" && request.method == "GET" -> {
86
val usersList = users.values.joinToString(",", "[", "]")
87
MockResponse()
88
.setResponseCode(200)
89
.addHeader("Content-Type", "application/json")
90
.setBody(usersList)
91
}
92
93
path == "/api/users" && request.method == "POST" -> {
94
val body = request.body.readUtf8()
95
// Simulate user creation
96
MockResponse()
97
.setResponseCode(201)
98
.addHeader("Content-Type", "application/json")
99
.addHeader("Location", "/api/users/3")
100
.setBody("""{"id": 3, "name": "New User"}""")
101
}
102
103
else -> MockResponse().setResponseCode(404)
104
}
105
}
106
}
107
108
// Apply custom dispatcher
109
val server = MockWebServer()
110
server.dispatcher = ApiDispatcher()
111
server.start()
112
```
113
114
**QueueDispatcher Advanced Configuration:**
115
116
```kotlin
117
val server = MockWebServer()
118
val queueDispatcher = QueueDispatcher()
119
120
// Configure fail-fast behavior
121
queueDispatcher.setFailFast(true) // Return 404 when queue is empty
122
// Or provide custom fail-fast response
123
queueDispatcher.setFailFast(
124
MockResponse()
125
.setResponseCode(503)
126
.setBody("Service temporarily unavailable")
127
)
128
129
server.dispatcher = queueDispatcher
130
server.start()
131
```
132
133
### HTTP/2 Server Push
134
135
Configure HTTP/2 server push promises to simulate modern web server behavior.
136
137
```kotlin { .api }
138
/**
139
* Represents an HTTP/2 server push promise
140
*/
141
class PushPromise(
142
/**
143
* HTTP method for the pushed request
144
*/
145
val method: String,
146
147
/**
148
* Path for the pushed request
149
*/
150
val path: String,
151
152
/**
153
* Headers for the pushed request
154
*/
155
val headers: Headers,
156
157
/**
158
* Response to be sent for the pushed request
159
*/
160
val response: MockResponse
161
)
162
163
/**
164
* Add HTTP/2 server push promise to response
165
* @param promise PushPromise to add
166
* @returns This MockResponse for method chaining
167
*/
168
fun withPush(promise: PushPromise): MockResponse
169
170
/**
171
* List of server push promises (read-only)
172
*/
173
val pushPromises: List<PushPromise>
174
```
175
176
**Usage Examples:**
177
178
```kotlin
179
val server = MockWebServer()
180
181
// Main page response with pushed resources
182
val cssContent = "body { font-family: Arial; }"
183
val jsContent = "console.log('App loaded');"
184
185
val mainPageResponse = MockResponse()
186
.setResponseCode(200)
187
.addHeader("Content-Type", "text/html")
188
.setBody("""
189
<!DOCTYPE html>
190
<html>
191
<head>
192
<link rel="stylesheet" href="/style.css">
193
<script src="/app.js"></script>
194
</head>
195
<body><h1>Hello World</h1></body>
196
</html>
197
""".trimIndent())
198
.withPush(
199
PushPromise(
200
method = "GET",
201
path = "/style.css",
202
headers = headingsOf("Content-Type", "text/css"),
203
response = MockResponse()
204
.setResponseCode(200)
205
.addHeader("Content-Type", "text/css")
206
.setBody(cssContent)
207
)
208
)
209
.withPush(
210
PushPromise(
211
method = "GET",
212
path = "/app.js",
213
headers = headersOf("Content-Type", "application/javascript"),
214
response = MockResponse()
215
.setResponseCode(200)
216
.addHeader("Content-Type", "application/javascript")
217
.setBody(jsContent)
218
)
219
)
220
221
server.enqueue(mainPageResponse)
222
server.start()
223
```
224
225
### Duplex Streaming (HTTP/2)
226
227
Advanced HTTP/2 bidirectional streaming capabilities for testing duplex communication.
228
229
```kotlin { .api }
230
/**
231
* Interface for duplex HTTP/2 response bodies (bidirectional streaming)
232
*/
233
interface DuplexResponseBody {
234
/**
235
* Handle incoming duplex request with HTTP/2 stream
236
* @param request RecordedRequest with request details
237
* @param http2Stream HTTP/2 stream for bidirectional communication
238
*/
239
fun onRequest(request: RecordedRequest, http2Stream: Http2Stream)
240
}
241
242
/**
243
* Scriptable duplex response for HTTP/2 bidirectional streaming
244
*/
245
class MockDuplexResponseBody : DuplexResponseBody {
246
/**
247
* Expect specific request content
248
* @param expected Expected request content string
249
* @returns This MockDuplexResponseBody for method chaining
250
*/
251
fun receiveRequest(expected: String): MockDuplexResponseBody
252
253
/**
254
* Expect request stream to be exhausted
255
* @returns This MockDuplexResponseBody for method chaining
256
*/
257
fun exhaustRequest(): MockDuplexResponseBody
258
259
/**
260
* Cancel stream with HTTP/2 error code
261
* @param errorCode HTTP/2 error code for cancellation
262
* @returns This MockDuplexResponseBody for method chaining
263
*/
264
fun cancelStream(errorCode: ErrorCode): MockDuplexResponseBody
265
266
/**
267
* Expect IOException when reading request
268
* @returns This MockDuplexResponseBody for method chaining
269
*/
270
fun requestIOException(): MockDuplexResponseBody
271
272
/**
273
* Send response data to client
274
* @param s Response data string
275
* @param responseSent Optional CountDownLatch for synchronization
276
* @returns This MockDuplexResponseBody for method chaining
277
*/
278
fun sendResponse(s: String, responseSent: CountDownLatch = CountDownLatch(0)): MockDuplexResponseBody
279
280
/**
281
* Close response stream
282
* @returns This MockDuplexResponseBody for method chaining
283
*/
284
fun exhaustResponse(): MockDuplexResponseBody
285
286
/**
287
* Add delay in conversation
288
* @param duration Delay duration
289
* @param unit Time unit for duration
290
* @returns This MockDuplexResponseBody for method chaining
291
*/
292
fun sleep(duration: Long, unit: TimeUnit): MockDuplexResponseBody
293
294
/**
295
* Wait for entire duplex conversation to complete successfully
296
*/
297
fun awaitSuccess()
298
}
299
300
/**
301
* Whether response is duplex (HTTP/2 bidirectional, read-only)
302
*/
303
val isDuplex: Boolean
304
```
305
306
**Usage Examples:**
307
308
```kotlin
309
val server = MockWebServer()
310
311
// Create duplex response body for chat-like interaction
312
val duplexBody = MockDuplexResponseBody()
313
.receiveRequest("HELLO")
314
.sendResponse("WELCOME")
315
.receiveRequest("GET_DATA")
316
.sendResponse("DATA: [1,2,3]")
317
.receiveRequest("BYE")
318
.sendResponse("GOODBYE")
319
.exhaustRequest()
320
.exhaustResponse()
321
322
val duplexResponse = MockResponse()
323
.setResponseCode(200)
324
.withDuplexBody(duplexBody)
325
326
server.enqueue(duplexResponse)
327
server.start()
328
329
// Client would establish HTTP/2 connection and stream data...
330
// After client interaction completes:
331
duplexBody.awaitSuccess() // Verify conversation completed as scripted
332
```
333
334
### SSL/TLS Configuration
335
336
Configure HTTPS with SSL certificates and client authentication options.
337
338
```kotlin { .api }
339
/**
340
* Enable HTTPS with SSL socket factory
341
* @param sslSocketFactory SSL socket factory for creating SSL connections
342
* @param tunnelProxy Whether to tunnel through proxy
343
*/
344
fun useHttps(sslSocketFactory: SSLSocketFactory, tunnelProxy: Boolean)
345
346
/**
347
* Disable SSL client authentication (default)
348
*/
349
fun noClientAuth()
350
351
/**
352
* Request (but don't require) SSL client authentication
353
*/
354
fun requestClientAuth()
355
356
/**
357
* Require SSL client authentication
358
*/
359
fun requireClientAuth()
360
```
361
362
**Usage Examples:**
363
364
```kotlin
365
val server = MockWebServer()
366
367
// Create SSL context with self-signed certificate
368
val sslContext = SSLContext.getInstance("TLS")
369
val keyManager = createSelfSignedKeyManager() // Your implementation
370
val trustManager = createTrustAllManager() // Your implementation
371
sslContext.init(arrayOf(keyManager), arrayOf(trustManager), SecureRandom())
372
373
// Enable HTTPS
374
server.useHttps(sslContext.socketFactory, false)
375
server.requestClientAuth() // Optional client certificates
376
377
server.enqueue(MockResponse().setBody("Secure response"))
378
server.start()
379
380
// Now server accepts HTTPS connections
381
val httpsUrl = server.url("/secure").newBuilder().scheme("https").build()
382
```
383
384
### Protocol Negotiation
385
386
Configure HTTP protocol version support and ALPN negotiation.
387
388
```kotlin { .api }
389
/**
390
* Whether ALPN protocol negotiation is enabled (default: true)
391
*/
392
var protocolNegotiationEnabled: Boolean
393
394
/**
395
* Supported protocols for ALPN (default: HTTP/2, HTTP/1.1)
396
*/
397
var protocols: List<Protocol>
398
```
399
400
**Usage Examples:**
401
402
```kotlin
403
val server = MockWebServer()
404
405
// Configure protocol support
406
server.protocolNegotiationEnabled = true
407
server.protocols = listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)
408
409
// Or disable HTTP/2, use only HTTP/1.1
410
server.protocols = listOf(Protocol.HTTP_1_1)
411
412
server.start()
413
```
414
415
### WebSocket Upgrades
416
417
Enable WebSocket protocol upgrades for testing WebSocket clients.
418
419
```kotlin { .api }
420
/**
421
* Enable WebSocket upgrade with listener
422
* @param listener WebSocketListener for handling WebSocket events
423
* @returns This MockResponse for method chaining
424
*/
425
fun withWebSocketUpgrade(listener: WebSocketListener): MockResponse
426
```
427
428
**Usage Examples:**
429
430
```kotlin
431
val server = MockWebServer()
432
433
val webSocketListener = object : WebSocketListener() {
434
override fun onOpen(webSocket: WebSocket, response: Response) {
435
webSocket.send("Welcome to WebSocket")
436
}
437
438
override fun onMessage(webSocket: WebSocket, text: String) {
439
when (text) {
440
"ping" -> webSocket.send("pong")
441
"close" -> webSocket.close(1000, "Normal closure")
442
else -> webSocket.send("Echo: $text")
443
}
444
}
445
}
446
447
val webSocketResponse = MockResponse()
448
.withWebSocketUpgrade(webSocketListener)
449
450
server.enqueue(webSocketResponse)
451
server.start()
452
453
// Client connects with WebSocket upgrade request
454
val request = Request.Builder()
455
.url(server.url("/websocket"))
456
.addHeader("Upgrade", "websocket")
457
.addHeader("Connection", "Upgrade")
458
.addHeader("Sec-WebSocket-Key", "generated-key")
459
.addHeader("Sec-WebSocket-Version", "13")
460
.build()
461
```
462
463
### HTTP/2 Settings and Informational Responses
464
465
Configure HTTP/2 settings frames and 1xx informational responses.
466
467
```kotlin { .api }
468
/**
469
* Set HTTP/2 settings frame
470
* @param settings HTTP/2 settings to apply
471
* @returns This MockResponse for method chaining
472
*/
473
fun withSettings(settings: Settings): MockResponse
474
475
/**
476
* Add 1xx informational response before main response
477
* @param informationalResponse MockResponse with 1xx status code
478
* @returns This MockResponse for method chaining
479
*/
480
fun addInformationalResponse(informationalResponse: MockResponse): MockResponse
481
```
482
483
**Usage Examples:**
484
485
```kotlin
486
// HTTP/2 settings configuration
487
val settingsFrame = Settings().apply {
488
set(Settings.MAX_CONCURRENT_STREAMS, 100)
489
set(Settings.INITIAL_WINDOW_SIZE, 65535)
490
}
491
492
val http2Response = MockResponse()
493
.setResponseCode(200)
494
.withSettings(settingsFrame)
495
.setBody("HTTP/2 response with custom settings")
496
497
// 1xx informational responses
498
val mainResponse = MockResponse()
499
.addInformationalResponse(
500
MockResponse().setResponseCode(100) // 100 Continue
501
)
502
.addInformationalResponse(
503
MockResponse().setResponseCode(102) // 102 Processing
504
)
505
.setResponseCode(200)
506
.setBody("Final response after informational responses")
507
508
server.enqueue(mainResponse)
509
```
510
511
### Duplex Streaming
512
513
MockDuplexResponseBody enables scriptable bidirectional HTTP/2 streaming for testing duplex communication patterns.
514
515
```kotlin { .api }
516
/**
517
* A scriptable request/response conversation for HTTP/2 duplex streaming.
518
* Create conversation scripts by calling methods in the sequence they should execute.
519
*/
520
class MockDuplexResponseBody : DuplexResponseBody {
521
/**
522
* Expect to receive specific request data
523
* @param expected Expected request content as string
524
* @returns This MockDuplexResponseBody for method chaining
525
*/
526
fun receiveRequest(expected: String): MockDuplexResponseBody
527
528
/**
529
* Expect the request body to be exhausted (no more data)
530
* @returns This MockDuplexResponseBody for method chaining
531
*/
532
fun exhaustRequest(): MockDuplexResponseBody
533
534
/**
535
* Cancel the HTTP/2 stream with specified error code
536
* @param errorCode HTTP/2 error code for stream cancellation
537
* @returns This MockDuplexResponseBody for method chaining
538
*/
539
fun cancelStream(errorCode: ErrorCode): MockDuplexResponseBody
540
541
/**
542
* Expect an IOException when reading request data
543
* @returns This MockDuplexResponseBody for method chaining
544
*/
545
fun requestIOException(): MockDuplexResponseBody
546
547
/**
548
* Send response data to client
549
* @param s Response content as string
550
* @param responseSent Optional latch signaled when response is sent
551
* @returns This MockDuplexResponseBody for method chaining
552
*/
553
fun sendResponse(s: String, responseSent: CountDownLatch = CountDownLatch(0)): MockDuplexResponseBody
554
555
/**
556
* Close the response stream (no more data will be sent)
557
* @returns This MockDuplexResponseBody for method chaining
558
*/
559
fun exhaustResponse(): MockDuplexResponseBody
560
561
/**
562
* Sleep for specified duration during conversation
563
* @param duration Duration to sleep
564
* @param unit Time unit for duration
565
* @returns This MockDuplexResponseBody for method chaining
566
*/
567
fun sleep(duration: Long, unit: TimeUnit): MockDuplexResponseBody
568
569
/**
570
* Wait for duplex conversation to complete successfully
571
* Blocks until all scripted actions are executed
572
*/
573
fun awaitSuccess()
574
}
575
```
576
577
**Usage Examples:**
578
579
```kotlin
580
import okhttp3.internal.http2.ErrorCode
581
import java.util.concurrent.CountDownLatch
582
import java.util.concurrent.TimeUnit
583
584
// Create duplex conversation script
585
val duplexBody = MockDuplexResponseBody()
586
.receiveRequest("Initial request data")
587
.sendResponse("Server acknowledges")
588
.receiveRequest("Follow-up data")
589
.sendResponse("Processing complete")
590
.exhaustRequest()
591
.exhaustResponse()
592
593
// Create duplex response
594
val duplexResponse = MockResponse()
595
.setResponseCode(200)
596
.addHeader("Content-Type", "application/octet-stream")
597
.setDuplexResponseBody(duplexBody)
598
599
server.enqueue(duplexResponse)
600
601
// Client performs duplex communication...
602
603
// Wait for conversation to complete
604
duplexBody.awaitSuccess()
605
606
// Stream cancellation example
607
val cancellingDuplexBody = MockDuplexResponseBody()
608
.receiveRequest("Start streaming")
609
.sendResponse("Streaming started")
610
.cancelStream(ErrorCode.CANCEL)
611
612
// Error handling example
613
val errorDuplexBody = MockDuplexResponseBody()
614
.receiveRequest("Valid data")
615
.sendResponse("Data received")
616
.requestIOException() // Expect IOException on next read
617
.exhaustResponse()
618
```