or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdindex.mdrequest-verification.mdresponse-configuration.mdserver-management.md

advanced-features.mddocs/

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

```