or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

frame-operations.mdindex.mdplugin-configuration.mdserialization-support.mdsession-management.mdwebsocket-connections.md

frame-operations.mddocs/

0

# Frame Operations

1

2

Low-level WebSocket frame handling for text, binary, and control frame processing with full control over WebSocket protocol features and frame-level operations.

3

4

## Capabilities

5

6

### Frame Base Class

7

8

Base sealed class for all WebSocket frame types with common properties and frame metadata.

9

10

```kotlin { .api }

11

/**

12

* Base sealed class for WebSocket frames

13

* Not reusable and not thread-safe

14

* @param fin Final fragment flag - true for complete frames

15

* @param frameType Type of the WebSocket frame

16

* @param data Frame payload data

17

* @param disposableHandle Handle for frame cleanup

18

* @param rsv1 First extension bit

19

* @param rsv2 Second extension bit

20

* @param rsv3 Third extension bit

21

*/

22

sealed class Frame private constructor(

23

fin: Boolean,

24

frameType: FrameType,

25

data: ByteArray,

26

disposableHandle: DisposableHandle = NonDisposableHandle,

27

rsv1: Boolean = false,

28

rsv2: Boolean = false,

29

rsv3: Boolean = false

30

) {

31

/** Final fragment flag - should be true for control frames */

32

val fin: Boolean

33

34

/** Frame type enumeration */

35

val frameType: FrameType

36

37

/** Frame payload data */

38

val data: ByteArray

39

40

/** Handle for frame disposal/cleanup */

41

val disposableHandle: DisposableHandle

42

43

/** First extension bit for WebSocket extensions */

44

val rsv1: Boolean

45

46

/** Second extension bit for WebSocket extensions */

47

val rsv2: Boolean

48

49

/** Third extension bit for WebSocket extensions */

50

val rsv3: Boolean

51

}

52

```

53

54

### Text Frame

55

56

Application-level text frame for UTF-8 encoded string messages.

57

58

```kotlin { .api }

59

/**

60

* Text frame for UTF-8 encoded string messages

61

* Can be fragmented in RAW WebSocket sessions (fin=false)

62

*/

63

class Frame.Text : Frame {

64

/**

65

* Create text frame with fragment control and extension bits

66

* @param fin Final fragment flag

67

* @param data UTF-8 encoded text data

68

* @param rsv1 First extension bit

69

* @param rsv2 Second extension bit

70

* @param rsv3 Third extension bit

71

*/

72

constructor(

73

fin: Boolean,

74

data: ByteArray,

75

rsv1: Boolean = false,

76

rsv2: Boolean = false,

77

rsv3: Boolean = false

78

)

79

80

/**

81

* Create complete text frame from byte array

82

* @param fin Final fragment flag

83

* @param data UTF-8 encoded text data

84

*/

85

constructor(fin: Boolean, data: ByteArray)

86

87

/**

88

* Create complete text frame from string

89

* @param text Text content (automatically UTF-8 encoded)

90

*/

91

constructor(text: String)

92

93

/**

94

* Create text frame from Source

95

* @param fin Final fragment flag

96

* @param packet Source containing UTF-8 text data

97

*/

98

constructor(fin: Boolean, packet: Source)

99

}

100

```

101

102

**Usage Examples:**

103

104

```kotlin

105

// Create text frame from string

106

val textFrame = Frame.Text("Hello WebSocket!")

107

108

// Create fragmented text frame

109

val fragment1 = Frame.Text(false, "Hello ".toByteArray())

110

val fragment2 = Frame.Text(true, "World!".toByteArray())

111

112

// Send text frame

113

session.outgoing.send(Frame.Text("Message content"))

114

115

// Create text frame with extension bits

116

val compressedText = Frame.Text(

117

fin = true,

118

data = compressedData,

119

rsv1 = true // Indicate compression extension

120

)

121

```

122

123

### Binary Frame

124

125

Application-level binary frame for raw byte data transmission.

126

127

```kotlin { .api }

128

/**

129

* Binary frame for raw byte data

130

* Can be fragmented in RAW WebSocket sessions (fin=false)

131

*/

132

class Frame.Binary : Frame {

133

/**

134

* Create binary frame with fragment control and extension bits

135

* @param fin Final fragment flag

136

* @param data Binary payload data

137

* @param rsv1 First extension bit

138

* @param rsv2 Second extension bit

139

* @param rsv3 Third extension bit

140

*/

141

constructor(

142

fin: Boolean,

143

data: ByteArray,

144

rsv1: Boolean = false,

145

rsv2: Boolean = false,

146

rsv3: Boolean = false

147

)

148

149

/**

150

* Create complete binary frame from byte array

151

* @param fin Final fragment flag

152

* @param data Binary payload data

153

*/

154

constructor(fin: Boolean, data: ByteArray)

155

156

/**

157

* Create binary frame from Source

158

* @param fin Final fragment flag

159

* @param packet Source containing binary data

160

*/

161

constructor(fin: Boolean, packet: Source)

162

}

163

```

164

165

**Usage Examples:**

166

167

```kotlin

168

// Create binary frame from byte array

169

val imageData = loadImageBytes()

170

val binaryFrame = Frame.Binary(true, imageData)

171

172

// Send binary frame

173

session.outgoing.send(Frame.Binary(true, fileBytes))

174

175

// Create fragmented binary frame

176

val chunk1 = Frame.Binary(false, data.sliceArray(0..1023))

177

val chunk2 = Frame.Binary(true, data.sliceArray(1024..data.size))

178

```

179

180

### Control Frames

181

182

Control frames for WebSocket protocol management (Close, Ping, Pong).

183

184

```kotlin { .api }

185

/**

186

* Close frame for connection termination

187

* Must not be fragmented (fin always true)

188

*/

189

class Frame.Close : Frame

190

191

/**

192

* Ping frame for connection keep-alive

193

* Must not be fragmented (fin always true)

194

*/

195

class Frame.Ping : Frame

196

197

/**

198

* Pong frame for ping response

199

* Must not be fragmented (fin always true)

200

*/

201

class Frame.Pong : Frame

202

```

203

204

**Usage Examples:**

205

206

```kotlin

207

// Send ping with payload

208

session.outgoing.send(Frame.Ping(byteArrayOf(1, 2, 3)))

209

210

// Respond to ping with pong

211

when (val frame = session.incoming.receive()) {

212

is Frame.Ping -> session.outgoing.send(Frame.Pong(frame.data))

213

}

214

215

// Close connection with reason

216

session.outgoing.send(Frame.Close())

217

```

218

219

### Frame Type Enumeration

220

221

Enumeration defining WebSocket frame types with protocol opcodes.

222

223

```kotlin { .api }

224

/**

225

* WebSocket frame type enumeration

226

* @param controlFrame Whether this is a control frame type

227

* @param opcode Frame type opcode for wire protocol

228

*/

229

enum class FrameType(val controlFrame: Boolean, val opcode: Int) {

230

/** Application level text frame */

231

TEXT(false, 1),

232

233

/** Application level binary frame */

234

BINARY(false, 2),

235

236

/** Control close frame */

237

CLOSE(true, 8),

238

239

/** Control ping frame */

240

PING(true, 9),

241

242

/** Control pong frame */

243

PONG(true, 0xa)

244

245

companion object {

246

/**

247

* Find FrameType by numeric opcode

248

* @param opcode Frame type opcode

249

* @return FrameType instance or null if invalid

250

*/

251

operator fun get(opcode: Int): FrameType?

252

}

253

}

254

```

255

256

### Frame Reading Functions

257

258

Utility functions for extracting data from frames.

259

260

```kotlin { .api }

261

/**

262

* Read text content from text frame as UTF-8 string

263

* @return Decoded text content

264

*/

265

fun Frame.Text.readText(): String

266

267

/**

268

* Read raw bytes from any frame type

269

* @return Frame payload data

270

*/

271

fun Frame.readBytes(): ByteArray

272

273

/**

274

* Read close reason from close frame

275

* @return CloseReason with code and message, or null if no reason

276

*/

277

fun Frame.Close.readReason(): CloseReason?

278

```

279

280

**Usage Examples:**

281

282

```kotlin

283

when (val frame = session.incoming.receive()) {

284

is Frame.Text -> {

285

val message = frame.readText()

286

println("Text message: $message")

287

}

288

289

is Frame.Binary -> {

290

val bytes = frame.readBytes()

291

println("Binary data: ${bytes.size} bytes")

292

}

293

294

is Frame.Close -> {

295

val reason = frame.readReason()

296

println("Connection closed: ${reason?.message}")

297

}

298

}

299

```

300

301

### Close Reason

302

303

Structure for WebSocket close frame reasons.

304

305

```kotlin { .api }

306

/**

307

* WebSocket close reason with code and message

308

* @param code Numeric close code (RFC 6455)

309

* @param message Optional human-readable message

310

*/

311

data class CloseReason(val code: Short, val message: String) {

312

313

/** Constructor using predefined codes */

314

constructor(code: Codes, message: String)

315

316

/** Get enum value for this code or null */

317

val knownReason: Codes?

318

319

/** Standard close reason codes */

320

enum class Codes(val code: Short) {

321

/** Normal closure (1000) */

322

NORMAL(1000),

323

324

/** Going away (1001) */

325

GOING_AWAY(1001),

326

327

/** Protocol error (1002) */

328

PROTOCOL_ERROR(1002),

329

330

/** Cannot accept data type (1003) */

331

CANNOT_ACCEPT(1003),

332

333

/** Not consistent UTF-8 (1007) */

334

NOT_CONSISTENT(1007),

335

336

/** Policy violation (1008) */

337

VIOLATED_POLICY(1008),

338

339

/** Message too big (1009) */

340

TOO_BIG(1009),

341

342

/** No extension (1010) */

343

NO_EXTENSION(1010),

344

345

/** Internal error (1011) */

346

INTERNAL_ERROR(1011),

347

348

/** Service restart (1012) */

349

SERVICE_RESTART(1012),

350

351

/** Try again later (1013) */

352

TRY_AGAIN_LATER(1013);

353

354

companion object {

355

fun byCode(code: Short): Codes?

356

}

357

}

358

}

359

```

360

361

## Frame Operation Examples

362

363

### Basic Frame Handling

364

365

```kotlin

366

client.webSocket("ws://echo.websocket.org") {

367

// Send different frame types

368

outgoing.send(Frame.Text("Hello WebSocket!"))

369

outgoing.send(Frame.Binary(true, byteArrayOf(1, 2, 3, 4)))

370

371

// Process incoming frames

372

for (frame in incoming) {

373

when (frame) {

374

is Frame.Text -> {

375

val text = frame.readText()

376

println("Received text: $text")

377

}

378

379

is Frame.Binary -> {

380

val bytes = frame.readBytes()

381

println("Received ${bytes.size} bytes")

382

}

383

384

is Frame.Close -> {

385

val reason = frame.readReason()

386

println("Connection closed: ${reason?.code} - ${reason?.message}")

387

break

388

}

389

390

is Frame.Ping -> {

391

// Respond to ping

392

outgoing.send(Frame.Pong(frame.data))

393

println("Responded to ping")

394

}

395

396

is Frame.Pong -> {

397

println("Received pong response")

398

}

399

}

400

}

401

}

402

```

403

404

### File Transfer Example

405

406

```kotlin

407

client.webSocket("ws://fileserver.example.com") {

408

// Send file as binary frames

409

val fileBytes = File("large-file.dat").readBytes()

410

val chunkSize = 64 * 1024 // 64KB chunks

411

412

for (i in fileBytes.indices step chunkSize) {

413

val chunk = fileBytes.sliceArray(i until minOf(i + chunkSize, fileBytes.size))

414

val isLast = i + chunkSize >= fileBytes.size

415

416

outgoing.send(Frame.Binary(isLast, chunk))

417

}

418

419

// Wait for acknowledgment

420

val ack = incoming.receive()

421

if (ack is Frame.Text && ack.readText() == "ACK") {

422

println("File transfer completed")

423

}

424

}

425

```

426

427

### JSON Message Protocol

428

429

```kotlin

430

client.webSocket("ws://api.example.com/json") {

431

// Send JSON as text frames

432

val request = JsonObject(mapOf(

433

"action" to JsonPrimitive("subscribe"),

434

"channel" to JsonPrimitive("trades")

435

))

436

437

outgoing.send(Frame.Text(request.toString()))

438

439

// Process JSON responses

440

for (frame in incoming) {

441

when (frame) {

442

is Frame.Text -> {

443

val json = Json.parseToJsonElement(frame.readText())

444

handleJsonMessage(json)

445

}

446

447

is Frame.Close -> break

448

}

449

}

450

}

451

```

452

453

### Extension Bit Usage

454

455

```kotlin

456

client.webSocket("ws://compressed.example.com") {

457

// Send compressed frame (using rsv1 for compression flag)

458

val originalData = "Large text content...".toByteArray()

459

val compressedData = compress(originalData)

460

461

outgoing.send(Frame.Text(

462

fin = true,

463

data = compressedData,

464

rsv1 = true // Indicate compression

465

))

466

467

// Handle compressed incoming frames

468

for (frame in incoming) {

469

when (frame) {

470

is Frame.Text -> {

471

val data = if (frame.rsv1) {

472

decompress(frame.data)

473

} else {

474

frame.data

475

}

476

477

val text = String(data, Charsets.UTF_8)

478

println("Message: $text")

479

}

480

}

481

}

482

}

483

```

484

485

### Custom Protocol Implementation

486

487

```kotlin

488

client.webSocket("ws://custom.example.com") {

489

// Custom protocol: first byte indicates message type

490

fun sendCustomMessage(type: Byte, payload: String) {

491

val data = byteArrayOf(type) + payload.toByteArray()

492

outgoing.send(Frame.Binary(true, data))

493

}

494

495

// Send different message types

496

sendCustomMessage(0x01, "User message")

497

sendCustomMessage(0x02, "System command")

498

499

// Process custom protocol messages

500

for (frame in incoming) {

501

when (frame) {

502

is Frame.Binary -> {

503

val data = frame.readBytes()

504

if (data.isNotEmpty()) {

505

val type = data[0]

506

val payload = String(data, 1, data.size - 1)

507

508

when (type.toInt()) {

509

0x01 -> handleUserMessage(payload)

510

0x02 -> handleSystemCommand(payload)

511

0x03 -> handleErrorMessage(payload)

512

}

513

}

514

}

515

516

is Frame.Close -> break

517

}

518

}

519

}

520

```

521

522

### Graceful Connection Closure

523

524

```kotlin

525

client.webSocket("ws://api.example.com") {

526

try {

527

// Normal operation

528

for (frame in incoming) {

529

when (frame) {

530

is Frame.Text -> processMessage(frame.readText())

531

is Frame.Close -> {

532

val reason = frame.readReason()

533

println("Server closed connection: ${reason?.message}")

534

break

535

}

536

}

537

}

538

} finally {

539

// Send close frame before terminating

540

try {

541

outgoing.send(Frame.Close())

542

} catch (e: Exception) {

543

// Channel might be already closed

544

}

545

}

546

}

547

```