1Recipes 2======= 3 4We've written some recipes that demonstrate how to solve common problems with Okio. Read through 5them to learn about how everything works together. Cut-and-paste these examples freely; that's what 6they're for. 7 8These recipes work on all platforms: Java, Android, Kotlin/Native, and Kotlin/JS. See 9[java.io Recipes](java_io_recipes.md) for samples that integrate Java APIs. 10 11 12Read a text file line-by-line ([Java][ReadFileLineByLine]/[Kotlin][ReadFileLineByLineKt]) 13----------------------------------------------------------------------------------------- 14 15Use `FileSystem.source(Path)` to open a source stream to read a file. The returned `Source` 16interface is very small and has limited uses. Instead we wrap the source with a buffer. This has two 17benefits: 18 19 * **It makes the API more powerful.** Instead of the basic methods offered by `Source`, 20 `BufferedSource` has dozens of methods to address most common problems concisely. 21 22 * **It makes your program run faster.** Buffering allows Okio to get more done with fewer I/O 23 operations. 24 25Each `Source` that is opened needs to be closed. The code that opens the stream is responsible for 26making sure it is closed. 27 28=== "Java" 29 30 Here we use Java's `try` blocks to close our sources automatically. 31 32 ```java 33 public void readLines(Path path) throws IOException { 34 try (Source fileSource = FileSystem.SYSTEM.source(path); 35 BufferedSource bufferedFileSource = Okio.buffer(fileSource)) { 36 37 while (true) { 38 String line = bufferedFileSource.readUtf8Line(); 39 if (line == null) break; 40 41 if (line.contains("square")) { 42 System.out.println(line); 43 } 44 } 45 46 } 47 } 48 ``` 49 50=== "Kotlin" 51 52 This uses `use` to automatically close the streams. This prevents resource leaks, even if an 53 exception is thrown. 54 55 ```kotlin 56 fun readLines(path: Path) { 57 FileSystem.SYSTEM.source(path).use { fileSource -> 58 fileSource.buffer().use { bufferedFileSource -> 59 while (true) { 60 val line = bufferedFileSource.readUtf8Line() ?: break 61 if ("square" in line) { 62 println(line) 63 } 64 } 65 } 66 } 67 } 68 ``` 69 70 71The `readUtf8Line()` API reads all of the data until the next line delimiter – either `\n`, `\r\n`, 72or the end of the file. It returns that data as a string, omitting the delimiter at the end. When it 73encounters empty lines the method will return an empty string. If there isn’t any more data to read 74it will return null. 75 76 77=== "Java" 78 79 The above Java program can be written more compactly by inlining the `fileSource` variable and 80 by using a fancy `for` loop instead of a `while`: 81 82 ```java 83 public void readLines(Path path) throws IOException { 84 try (BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(path))) { 85 for (String line; (line = source.readUtf8Line()) != null; ) { 86 if (line.contains("square")) { 87 System.out.println(line); 88 } 89 } 90 } 91 } 92 ``` 93 94=== "Kotlin" 95 96 In Kotlin, we can use `FileSystem.read()` to buffer the source before our block and close the 97 source afterwards. In the body of the block, `this` is a `BufferedSource`. 98 99 ```kotlin 100 @Throws(IOException::class) 101 fun readLines(path: Path) { 102 FileSystem.SYSTEM.read(path) { 103 while (true) { 104 val line = readUtf8Line() ?: break 105 if ("square" in line) { 106 println(line) 107 } 108 } 109 } 110 } 111 ``` 112 113The `readUtf8Line()` method is suitable for parsing most files. For certain use-cases you may also 114consider `readUtf8LineStrict()`. It is similar but it requires that each line is terminated by `\n` 115or `\r\n`. If it encounters the end of the file before that it will throw an `EOFException`. The 116strict variant also permits a byte limit to defend against malformed input. 117 118=== "Java" 119 120 ```java 121 public void readLines(Path path) throws IOException { 122 try (BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(path))) { 123 while (!source.exhausted()) { 124 String line = source.readUtf8LineStrict(1024L); 125 if (line.contains("square")) { 126 System.out.println(line); 127 } 128 } 129 } 130 } 131 ``` 132 133=== "Kotlin" 134 135 ```kotlin 136 @Throws(IOException::class) 137 fun readLines(path: Path) { 138 FileSystem.SYSTEM.read(path) { 139 while (!source.exhausted()) { 140 val line = source.readUtf8LineStrict(1024) 141 if ("square" in line) { 142 println(line) 143 } 144 } 145 } 146 } 147 ``` 148 149 150Write a text file ([Java][WriteFile]/[Kotlin][WriteFileKt]) 151----------------------------------------------------------- 152 153Above we used a `Source` and a `BufferedSource` to read a file. To write, we use a `Sink` and a 154`BufferedSink`. The advantages of buffering are the same: a more capable API and better performance. 155 156```java 157public void writeEnv(Path path) throws IOException { 158 try (Sink fileSink = FileSystem.SYSTEM.sink(path); 159 BufferedSink bufferedSink = Okio.buffer(fileSink)) { 160 161 for (Map.Entry<String, String> entry : System.getenv().entrySet()) { 162 bufferedSink.writeUtf8(entry.getKey()); 163 bufferedSink.writeUtf8("="); 164 bufferedSink.writeUtf8(entry.getValue()); 165 bufferedSink.writeUtf8("\n"); 166 } 167 168 } 169} 170``` 171 172There isn’t an API to write a line of input; instead we manually insert our own newline character. 173Most programs should hardcode `"\n"` as the newline character. In rare situations you may use 174`System.lineSeparator()` instead of `"\n"`: it returns `"\r\n"` on Windows and `"\n"` everywhere 175else. 176 177=== "Java" 178 179 We can write the above program more compactly by inlining the `fileSink` variable and by taking 180 advantage of method chaining: 181 182 ```java 183 public void writeEnv(Path path) throws IOException { 184 try (BufferedSink sink = Okio.buffer(FileSystem.SYSTEM.sink(path))) { 185 for (Map.Entry<String, String> entry : System.getenv().entrySet()) { 186 sink.writeUtf8(entry.getKey()) 187 .writeUtf8("=") 188 .writeUtf8(entry.getValue()) 189 .writeUtf8("\n"); 190 } 191 } 192 } 193 ``` 194 195=== "Kotlin" 196 197 In Kotlin, we can use `FileSystem.write()` to buffer the sink before our block and close the 198 sink afterwards. In the body of the block, `this` is a `BufferedSink`. 199 200 ```kotlin 201 @Throws(IOException::class) 202 fun writeEnv(path: Path) { 203 FileSystem.SYSTEM.write(path) { 204 for ((key, value) in System.getenv()) { 205 writeUtf8(key) 206 writeUtf8("=") 207 writeUtf8(value) 208 writeUtf8("\n") 209 } 210 } 211 } 212 ``` 213 214In the above code we make four calls to `writeUtf8()`. Making four calls is more efficient than the 215code below because the VM doesn’t have to create and garbage collect a temporary string. 216 217```java 218sink.writeUtf8(entry.getKey() + "=" + entry.getValue() + "\n"); // Slower! 219``` 220 221 222UTF-8 ([Java][ExploreCharsets]/[Kotlin][ExploreCharsetsKt]) 223----------------------------------------------------------- 224 225In the above APIs you can see that Okio really likes UTF-8. Early computer systems suffered many 226incompatible character encodings: ISO-8859-1, ShiftJIS, ASCII, EBCDIC, etc. Writing software to 227support multiple character sets was awful and we didn’t even have emoji! Today we're lucky that the 228world has standardized on UTF-8 everywhere, with some rare uses of other charsets in legacy systems. 229 230If you need another character set, `readString()` and `writeString()` are there for you. These 231 methods require that you specify a character set. Otherwise you may accidentally create data that 232 is only readable by the local computer. Most programs should use the UTF-8 methods only. 233 234When encoding strings you need to be mindful of the different ways that strings are represented and 235encoded. When a glyph has an accent or another adornment it may be represented as a single complex 236 code point (`é`) or as a simple code point (`e`) followed by its modifiers (`´`). When the entire 237 glyph is a single code point that’s called [NFC][nfc]; when it’s multiple it’s [NFD][nfd]. 238 239Though we use UTF-8 whenever we read or write strings in I/O, when they are in memory Java Strings 240use an obsolete character encoding called UTF-16. It is a bad encoding because it uses a 16-bit 241`char` for most characters, but some don’t fit. In particular, most emoji use two Java chars. This 242is problematic because `String.length()` returns a surprising result: the number of UTF-16 chars and 243not the natural number of glyphs. 244 245| | Café | Café | 246| --------------------: | :---------------------------| :------------------------------| 247| Form | [NFC][nfc] | [NFD][nfd] | 248| Code Points | `c a f é ␣ ` | `c a f e ´ ␣ ` | 249| UTF-8 bytes | `43 61 66 c3a9 20 f09f8da9` | `43 61 66 65 cc81 20 f09f8da9` | 250| String.codePointCount | 6 | 7 | 251| String.length | 7 | 8 | 252| Utf8.size | 10 | 11 | 253 254For the most part Okio lets you ignore these problems and focus on your data. But when you need 255them, there are convenient APIs for dealing with low-level UTF-8 strings. 256 257Use `Utf8.size()` to count the number of bytes required to encode a string as UTF-8 without actually 258encoding it. This is handy in length-prefixed encodings like protocol buffers. 259 260Use `BufferedSource.readUtf8CodePoint()` to read a single variable-length code point, and 261`BufferedSink.writeUtf8CodePoint()` to write one. 262 263 264=== "Java" 265 266 ```java 267 public void dumpStringData(String s) throws IOException { 268 System.out.println(" " + s); 269 System.out.println(" String.length: " + s.length()); 270 System.out.println("String.codePointCount: " + s.codePointCount(0, s.length())); 271 System.out.println(" Utf8.size: " + Utf8.size(s)); 272 System.out.println(" UTF-8 bytes: " + ByteString.encodeUtf8(s).hex()); 273 System.out.println(); 274 } 275 ``` 276 277=== "Kotlin" 278 279 ```kotlin 280 fun dumpStringData(s: String) { 281 println(" " + s) 282 println(" String.length: " + s.length) 283 println("String.codePointCount: " + s.codePointCount(0, s.length)) 284 println(" Utf8.size: " + s.utf8Size()) 285 println(" UTF-8 bytes: " + s.encodeUtf8().hex()) 286 println() 287 } 288 ``` 289 290Golden Values ([Java][GoldenValue]/[Kotlin][GoldenValueKt]) 291----------------------------------------------------------- 292 293Okio likes testing. The library itself is heavily tested, and it has features that are often helpful 294when testing application code. One pattern we’ve found to be quite useful is “golden value” testing. 295The goal of such tests is to confirm that data encoded with earlier versions of a program can safely 296be decoded by the current program. 297 298We’ll illustrate this by encoding a value using Java Serialization. Though we must disclaim that 299Java Serialization is an awful encoding system and most programs should prefer other formats like 300JSON or protobuf! In any case, here’s a method that takes an object, serializes it, and returns the 301result as a `ByteString`: 302 303=== "Java" 304 305 ```Java 306 private ByteString serialize(Object o) throws IOException { 307 Buffer buffer = new Buffer(); 308 try (ObjectOutputStream objectOut = new ObjectOutputStream(buffer.outputStream())) { 309 objectOut.writeObject(o); 310 } 311 return buffer.readByteString(); 312 } 313 ``` 314 315=== "Kotlin" 316 317 ```Kotlin 318 @Throws(IOException::class) 319 private fun serialize(o: Any?): ByteString { 320 val buffer = Buffer() 321 ObjectOutputStream(buffer.outputStream()).use { objectOut -> 322 objectOut.writeObject(o) 323 } 324 return buffer.readByteString() 325 } 326 ``` 327 328There’s a lot going on here. 329 3301. We create a buffer as a holding space for our serialized data. It’s a convenient replacement for 331 `ByteArrayOutputStream`. 332 3332. We ask the buffer for its output stream. Writes to a buffer or its output stream always append 334 data to the end of the buffer. 335 3363. We create an `ObjectOutputStream` (the encoding API for Java serialization) and write our object. 337 The try block takes care of closing the stream for us. Note that closing a buffer has no effect. 338 3394. Finally we read a byte string from the buffer. The `readByteString()` method allows us to specify 340 how many bytes to read; here we don’t specify a count in order to read the entire thing. Reads 341 from a buffer always consume data from the front of the buffer. 342 343With our `serialize()` method handy we are ready to compute and print a golden value. 344 345=== "Java" 346 347 ```Java 348 Point point = new Point(8.0, 15.0); 349 ByteString pointBytes = serialize(point); 350 System.out.println(pointBytes.base64()); 351 ``` 352 353=== "Kotlin" 354 355 ```Kotlin 356 val point = Point(8.0, 15.0) 357 val pointBytes = serialize(point) 358 println(pointBytes.base64()) 359 ``` 360 361We print the `ByteString` as [base64][base64] because it’s a compact format that’s suitable for 362embedding in a test case. The program prints this: 363 364``` 365rO0ABXNyAB5va2lvLnNhbXBsZXMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA 366``` 367 368That’s our golden value! We can embed it in our test case using base64 again to convert it back into 369a `ByteString`: 370 371=== "Java" 372 373 ```Java 374 ByteString goldenBytes = ByteString.decodeBase64("rO0ABXNyAB5va2lvLnNhbXBsZ" 375 + "XMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuA" 376 + "AAAAAAA"); 377 ``` 378 379=== "Kotlin" 380 381 ```Kotlin 382 val goldenBytes = ("rO0ABXNyACRva2lvLnNhbXBsZXMuS290bGluR29sZGVuVmFsdWUkUG9pbnRF9yaY7cJ9EwIAA" + 383 "kQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA").decodeBase64() 384 ``` 385 386The next step is to deserialize the `ByteString` back into our value class. This method reverses the 387`serialize()` method above: we append a byte string to a buffer then consume it using an 388`ObjectInputStream`: 389 390=== "Java" 391 392 ```Java 393 private Object deserialize(ByteString byteString) throws IOException, ClassNotFoundException { 394 Buffer buffer = new Buffer(); 395 buffer.write(byteString); 396 try (ObjectInputStream objectIn = new ObjectInputStream(buffer.inputStream())) { 397 return objectIn.readObject(); 398 } 399 } 400 ``` 401 402=== "Kotlin" 403 404 ```Kotlin 405 @Throws(IOException::class, ClassNotFoundException::class) 406 private fun deserialize(byteString: ByteString): Any? { 407 val buffer = Buffer() 408 buffer.write(byteString) 409 ObjectInputStream(buffer.inputStream()).use { objectIn -> 410 return objectIn.readObject() 411 } 412 } 413 ``` 414 415Now we can test the decoder against the golden value: 416 417=== "Java" 418 419 ```Java 420 ByteString goldenBytes = ByteString.decodeBase64("rO0ABXNyAB5va2lvLnNhbXBsZ" 421 + "XMuR29sZGVuVmFsdWUkUG9pbnTdUW8rMji1IwIAAkQAAXhEAAF5eHBAIAAAAAAAAEAuA" 422 + "AAAAAAA"); 423 Point decoded = (Point) deserialize(goldenBytes); 424 assertEquals(new Point(8.0, 15.0), decoded); 425 ``` 426 427=== "Kotlin" 428 429 ```Kotlin 430 val goldenBytes = ("rO0ABXNyACRva2lvLnNhbXBsZXMuS290bGluR29sZGVuVmFsdWUkUG9pbnRF9yaY7cJ9EwIAA" + 431 "kQAAXhEAAF5eHBAIAAAAAAAAEAuAAAAAAAA").decodeBase64()!! 432 val decoded = deserialize(goldenBytes) as Point 433 assertEquals(point, decoded) 434 ``` 435 436With this test we can change the serialization of the `Point` class without 437breaking compatibility. 438 439 440Write a binary file ([Java][BitmapEncoder]/[Kotlin][BitmapEncoderKt]) 441--------------------------------------------------------------------- 442 443Encoding a binary file is not unlike encoding a text file. Okio uses the same `BufferedSink` and 444`BufferedSource` bytes for both. This is handy for binary formats that include both byte and 445character data. 446 447Writing binary data is more hazardous than text because if you make a mistake it is often quite 448difficult to diagnose. Avoid such mistakes by being careful around these traps: 449 450 * **The width of each field.** This is the number of bytes used. Okio doesn't include a mechanism 451 to emit partial bytes. If you need that, you’ll need to do your own bit shifting and masking 452 before writing. 453 454 * **The endianness of each field.** All fields that have more than one byte have _endianness_: 455 whether the bytes are ordered most-significant to least (big endian) or least-significant to most 456 (little endian). Okio uses the `Le` suffix for little-endian methods; methods without a suffix 457 are big-endian. 458 459 * **Signed vs. Unsigned.** Java doesn’t have unsigned primitive types (except for `char`!) so 460 coping with this is often something that happens at the application layer. To make this a little 461 easier Okio accepts `int` types for `writeByte()` and `writeShort()`. You can pass an “unsigned” 462 byte like 255 and Okio will do the right thing. 463 464| Method | Width | Endianness | Value | Encoded Value | 465| :----------- | ----: | :--------- | --------------: | :------------------------ | 466| writeByte | 1 | | 3 | `03` | 467| writeShort | 2 | big | 3 | `00 03` | 468| writeInt | 4 | big | 3 | `00 00 00 03` | 469| writeLong | 8 | big | 3 | `00 00 00 00 00 00 00 03` | 470| writeShortLe | 2 | little | 3 | `03 00` | 471| writeIntLe | 4 | little | 3 | `03 00 00 00` | 472| writeLongLe | 8 | little | 3 | `03 00 00 00 00 00 00 00` | 473| writeByte | 1 | | Byte.MAX_VALUE | `7f` | 474| writeShort | 2 | big | Short.MAX_VALUE | `7f ff` | 475| writeInt | 4 | big | Int.MAX_VALUE | `7f ff ff ff` | 476| writeLong | 8 | big | Long.MAX_VALUE | `7f ff ff ff ff ff ff ff` | 477| writeShortLe | 2 | little | Short.MAX_VALUE | `ff 7f` | 478| writeIntLe | 4 | little | Int.MAX_VALUE | `ff ff ff 7f` | 479| writeLongLe | 8 | little | Long.MAX_VALUE | `ff ff ff ff ff ff ff 7f` | 480 481This code encodes a bitmap following the [BMP file format][bmp]. 482 483=== "Java" 484 485 ```Java 486 void encode(Bitmap bitmap, BufferedSink sink) throws IOException { 487 int height = bitmap.height(); 488 int width = bitmap.width(); 489 490 int bytesPerPixel = 3; 491 int rowByteCountWithoutPadding = (bytesPerPixel * width); 492 int rowByteCount = ((rowByteCountWithoutPadding + 3) / 4) * 4; 493 int pixelDataSize = rowByteCount * height; 494 int bmpHeaderSize = 14; 495 int dibHeaderSize = 40; 496 497 // BMP Header 498 sink.writeUtf8("BM"); // ID. 499 sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize); // File size. 500 sink.writeShortLe(0); // Unused. 501 sink.writeShortLe(0); // Unused. 502 sink.writeIntLe(bmpHeaderSize + dibHeaderSize); // Offset of pixel data. 503 504 // DIB Header 505 sink.writeIntLe(dibHeaderSize); 506 sink.writeIntLe(width); 507 sink.writeIntLe(height); 508 sink.writeShortLe(1); // Color plane count. 509 sink.writeShortLe(bytesPerPixel * Byte.SIZE); 510 sink.writeIntLe(0); // No compression. 511 sink.writeIntLe(16); // Size of bitmap data including padding. 512 sink.writeIntLe(2835); // Horizontal print resolution in pixels/meter. (72 dpi). 513 sink.writeIntLe(2835); // Vertical print resolution in pixels/meter. (72 dpi). 514 sink.writeIntLe(0); // Palette color count. 515 sink.writeIntLe(0); // 0 important colors. 516 517 // Pixel data. 518 for (int y = height - 1; y >= 0; y--) { 519 for (int x = 0; x < width; x++) { 520 sink.writeByte(bitmap.blue(x, y)); 521 sink.writeByte(bitmap.green(x, y)); 522 sink.writeByte(bitmap.red(x, y)); 523 } 524 525 // Padding for 4-byte alignment. 526 for (int p = rowByteCountWithoutPadding; p < rowByteCount; p++) { 527 sink.writeByte(0); 528 } 529 } 530 } 531 ``` 532 533=== "Kotlin" 534 535 ```Kotlin 536 @Throws(IOException::class) 537 fun encode(bitmap: Bitmap, sink: BufferedSink) { 538 val height = bitmap.height 539 val width = bitmap.width 540 val bytesPerPixel = 3 541 val rowByteCountWithoutPadding = bytesPerPixel * width 542 val rowByteCount = (rowByteCountWithoutPadding + 3) / 4 * 4 543 val pixelDataSize = rowByteCount * height 544 val bmpHeaderSize = 14 545 val dibHeaderSize = 40 546 547 // BMP Header 548 sink.writeUtf8("BM") // ID. 549 sink.writeIntLe(bmpHeaderSize + dibHeaderSize + pixelDataSize) // File size. 550 sink.writeShortLe(0) // Unused. 551 sink.writeShortLe(0) // Unused. 552 sink.writeIntLe(bmpHeaderSize + dibHeaderSize) // Offset of pixel data. 553 554 // DIB Header 555 sink.writeIntLe(dibHeaderSize) 556 sink.writeIntLe(width) 557 sink.writeIntLe(height) 558 sink.writeShortLe(1) // Color plane count. 559 sink.writeShortLe(bytesPerPixel * Byte.SIZE_BITS) 560 sink.writeIntLe(0) // No compression. 561 sink.writeIntLe(16) // Size of bitmap data including padding. 562 sink.writeIntLe(2835) // Horizontal print resolution in pixels/meter. (72 dpi). 563 sink.writeIntLe(2835) // Vertical print resolution in pixels/meter. (72 dpi). 564 sink.writeIntLe(0) // Palette color count. 565 sink.writeIntLe(0) // 0 important colors. 566 567 // Pixel data. 568 for (y in height - 1 downTo 0) { 569 for (x in 0 until width) { 570 sink.writeByte(bitmap.blue(x, y)) 571 sink.writeByte(bitmap.green(x, y)) 572 sink.writeByte(bitmap.red(x, y)) 573 } 574 575 // Padding for 4-byte alignment. 576 for (p in rowByteCountWithoutPadding until rowByteCount) { 577 sink.writeByte(0) 578 } 579 } 580 } 581 ``` 582 583The trickiest part of this program is the format’s required padding. The BMP format expects each row 584to begin on a 4-byte boundary so it is necessary to add zeros to maintain the alignment. 585 586Encoding other binary formats is usually quite similar. Some tips: 587 588 * Write tests with golden values! Confirming that your program emits the expected result can make 589 debugging easier. 590 * Use `Utf8.size()` to compute the number of bytes of an encoded string. This is essential for 591 length-prefixed formats. 592 * Use `Float.floatToIntBits()` and `Double.doubleToLongBits()` to encode floating point values. 593 594 595Communicate on a Socket ([Java][SocksProxyServer]/[Kotlin][SocksProxyServerKt]) 596------------------------------------------------------------------------------- 597 598Note that Okio doesn't yet support sockets on Kotlin/Native or Kotlin/JS. 599 600Sending and receiving data over the network is a bit like writing and reading files. We use 601`BufferedSink` to encode output and `BufferedSource` to decode input. Like files, network protocols 602can be text, binary, or a mix of both. But there are also some substantial differences between the 603network and the file system. 604 605With a file you’re either reading or writing but with the network you can do both! Some protocols 606handle this by taking turns: write a request, read a response, repeat. You can implement this kind 607of protocol with a single thread. In other protocols you may read and write simultaneously. 608Typically you’ll want one dedicated thread for reading. For writing you can use either a dedicated 609thread or use `synchronized` so that multiple threads can share a sink. Okio’s streams are not safe 610for concurrent use. 611 612Sinks buffer outbound data to minimize I/O operations. This is efficient but it means you must 613manually call `flush()` to transmit data. Typically message-oriented protocols flush after each 614message. Note that Okio will automatically flush when the buffered data exceeds some threshold. This 615is intended to save memory and you shouldn’t rely on it for interactive protocols. 616 617Okio builds on `java.io.Socket` for connectivity. Create your socket as a server or as a client, 618then use `Okio.source(Socket)` to read and `Okio.sink(Socket)` to write. These APIs also work with 619`SSLSocket`. You should use SSL unless you have a very good reason not to! 620 621Cancel a socket from any thread by calling `Socket.close()`; this will cause its sources and sinks 622to immediately fail with an `IOException`. You can also configure timeouts for all socket 623operations. You don’t need a reference to the socket to adjust timeouts: `Source` and `Sink` expose 624timeouts directly. This API works even if the streams are decorated. 625 626As a complete example of networking with Okio we wrote a [basic SOCKS proxy][SocksProxyServer] 627server. Some highlights: 628 629=== "Java" 630 631 ```Java 632 Socket fromSocket = ... 633 BufferedSource fromSource = Okio.buffer(Okio.source(fromSocket)); 634 BufferedSink fromSink = Okio.buffer(Okio.sink(fromSocket)); 635 ``` 636 637=== "Kotlin" 638 639 ```Kotlin 640 val fromSocket: Socket = ... 641 val fromSource = fromSocket.source().buffer() 642 val fromSink = fromSocket.sink().buffer() 643 ``` 644 645Creating sources and sinks for sockets is the same as creating them for files. Once you create a 646`Source` or `Sink` for a socket you must not use its `InputStream` or `OutputStream`, respectively. 647 648=== "Java" 649 650 ```Java 651 Buffer buffer = new Buffer(); 652 for (long byteCount; (byteCount = source.read(buffer, 8192L)) != -1; ) { 653 sink.write(buffer, byteCount); 654 sink.flush(); 655 } 656 ``` 657 658=== "Kotlin" 659 660 ```Kotlin 661 val buffer = Buffer() 662 var byteCount: Long 663 while (source.read(buffer, 8192L).also { byteCount = it } != -1L) { 664 sink.write(buffer, byteCount) 665 sink.flush() 666 } 667 ``` 668 669The above loop copies data from the source to the sink, flushing after each read. If we didn’t need 670the flushing we could replace this loop with a single call to `BufferedSink.writeAll(Source)`. 671 672The `8192` argument to `read()` is the maximum number of bytes to read before returning. We could 673have passed any value here, but we like 8 KiB because that’s the largest value Okio can do in a 674single system call. Most of the time application code doesn’t need to deal with such limits! 675 676=== "Java" 677 678 ```Java 679 int addressType = fromSource.readByte() & 0xff; 680 int port = fromSource.readShort() & 0xffff; 681 ``` 682 683=== "Kotlin" 684 685 ```Kotlin 686 val addressType = fromSource.readByte().toInt() and 0xff 687 val port = fromSource.readShort().toInt() and 0xffff 688 ``` 689 690Okio uses signed types like `byte` and `short`, but often protocols want unsigned values. The 691bitwise `&` operator is Java’s preferred idiom to convert a signed value into an unsigned value. 692Here’s a cheat sheet for bytes, shorts, and ints: 693 694| Type | Signed Range | Unsigned Range | Signed to Unsigned | 695| :---- | :---------------------------: | :--------------- | :-------------------------- | 696| byte | -128..127 | 0..255 | `int u = s & 0xff;` | 697| short | -32,768..32,767 | 0..65,535 | `int u = s & 0xffff;` | 698| int | -2,147,483,648..2,147,483,647 | 0..4,294,967,295 | `long u = s & 0xffffffffL;` | 699 700Java has no primitive type that can represent unsigned longs. 701 702 703Hashing ([Java][Hashing]/[Kotlin][HashingKt]) 704--------------------------------------------- 705 706We’re bombarded by hashing in our lives as Java programmers. Early on we're introduced to the 707`hashCode()` method, something we know we need to override otherwise unforeseen bad things happen. 708Later we’re shown `LinkedHashMap` and its friends. These build on that `hashCode()` method to 709organize data for fast retrieval. 710 711Elsewhere we have cryptographic hash functions. These get used all over the place. HTTPS 712certificates, Git commits, BitTorrent integrity checking, and Blockchain blocks all use 713cryptographic hashes. Good use of hashes can improve the performance, privacy, security, and 714simplicity of an application. 715 716Each cryptographic hash function accepts a variable-length stream of input bytes and produces a 717fixed-length byte string value called the “hash”. Hash functions have these important qualities: 718 719 * Deterministic: each input always produces the same output. 720 * Uniform: each output byte string is equally likely. It is very difficult to find or create pairs 721 of different inputs that yield the same output. This is called a “collision”. 722 * Non-reversible: knowing an output doesn't help you to find the input. Note that if you know some 723 possible inputs you can hash them to see if their hashes match. 724 * Well-known: the hash is implemented everywhere and rigorously understood. 725 726Good hash functions are very cheap to compute (dozens of microseconds) and expensive to reverse 727(quintillions of millenia). Steady advances in computing and mathematics have caused once-great hash 728functions to become inexpensive to reverse. When choosing a hash function, beware that not all are 729created equal! Okio supports these well-known cryptographic hash functions: 730 731 * **MD5**: a 128-bit (16 byte) cryptographic hash. It is both insecure and obsolete because it is 732 inexpensive to reverse! This hash is offered because it is popular and convenient for use in 733 legacy systems that are not security-sensitive. 734 * **SHA-1**: a 160-bit (20 byte) cryptographic hash. It was recently demonstrated that it is 735 feasible to create SHA-1 collisions. Consider upgrading from SHA-1 to SHA-256. 736 * **SHA-256**: a 256-bit (32 byte) cryptographic hash. SHA-256 is widely understood and expensive 737 to reverse. This is the hash most systems should use. 738 * **SHA-512**: a 512-bit (64 byte) cryptographic hash. It is expensive to reverse. 739 740Each hash creates a `ByteString` of the specified length. Use `hex()` to get the conventional 741human-readable form. Or leave it as a `ByteString` because that’s a convenient model type! 742 743Okio can produce cryptographic hashes from byte strings: 744 745=== "Java" 746 747 ```Java 748 ByteString byteString = readByteString(Path.get("README.md")); 749 System.out.println(" md5: " + byteString.md5().hex()); 750 System.out.println(" sha1: " + byteString.sha1().hex()); 751 System.out.println("sha256: " + byteString.sha256().hex()); 752 System.out.println("sha512: " + byteString.sha512().hex()); 753 ``` 754 755=== "Kotlin" 756 757 ```Kotlin 758 val byteString = readByteString("README.md".toPath()) 759 println(" md5: " + byteString.md5().hex()) 760 println(" sha1: " + byteString.sha1().hex()) 761 println(" sha256: " + byteString.sha256().hex()) 762 println(" sha512: " + byteString.sha512().hex()) 763 ``` 764 765From buffers: 766 767=== "Java" 768 769 ```Java 770 Buffer buffer = readBuffer(Path.get("README.md")); 771 System.out.println(" md5: " + buffer.md5().hex()); 772 System.out.println(" sha1: " + buffer.sha1().hex()); 773 System.out.println("sha256: " + buffer.sha256().hex()); 774 System.out.println("sha512: " + buffer.sha512().hex()); 775 ``` 776 777=== "Kotlin" 778 779 ```Kotlin 780 val buffer = readBuffer("README.md".toPath()) 781 println(" md5: " + buffer.md5().hex()) 782 println(" sha1: " + buffer.sha1().hex()) 783 println(" sha256: " + buffer.sha256().hex()) 784 println(" sha512: " + buffer.sha512().hex()) 785 ``` 786 787While streaming from a source: 788 789=== "Java" 790 791 ```Java 792 try (HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); 793 BufferedSource source = Okio.buffer(FileSystem.SYSTEM.source(path))) { 794 source.readAll(hashingSink); 795 System.out.println("sha256: " + hashingSink.hash().hex()); 796 } 797 ``` 798 799=== "Kotlin" 800 801 ```Kotlin 802 sha256(blackholeSink()).use { hashingSink -> 803 FileSystem.SYSTEM.source(path).buffer().use { source -> 804 source.readAll(hashingSink) 805 println(" sha256: " + hashingSink.hash.hex()) 806 } 807 } 808 ``` 809 810While streaming to a sink: 811 812=== "Java" 813 814 ```Java 815 try (HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); 816 BufferedSink sink = Okio.buffer(hashingSink); 817 Source source = FileSystem.SYSTEM.source(path)) { 818 sink.writeAll(source); 819 sink.close(); // Emit anything buffered. 820 System.out.println("sha256: " + hashingSink.hash().hex()); 821 } 822 ``` 823 824=== "Kotlin" 825 826 ```Kotlin 827 sha256(blackholeSink()).use { hashingSink -> 828 hashingSink.buffer().use { sink -> 829 FileSystem.SYSTEM.source(path).use { source -> 830 sink.writeAll(source) 831 sink.close() // Emit anything buffered. 832 println(" sha256: " + hashingSink.hash.hex()) 833 } 834 } 835 } 836 ``` 837 838Okio also supports HMAC (Hash Message Authentication Code) which combines a secret and a hash. 839Applications use HMAC for data integrity and authentication. 840 841=== "Java" 842 843 ```Java 844 ByteString secret = ByteString.decodeHex("7065616e7574627574746572"); 845 System.out.println("hmacSha256: " + byteString.hmacSha256(secret).hex()); 846 ``` 847 848=== "Kotlin" 849 850 ```Kotlin 851 val secret = "7065616e7574627574746572".decodeHex() 852 println("hmacSha256: " + byteString.hmacSha256(secret).hex()) 853 ``` 854 855As with hashing, you can generate an HMAC from a `ByteString`, `Buffer`, `HashingSource`, and 856`HashingSink`. Note that Okio doesn’t implement HMAC for MD5. 857 858On Android and Java, Okio uses Java’s `java.security.MessageDigest` for cryptographic hashes and 859`javax.crypto.Mac` for HMAC. On other platforms Okio uses its own optimized implementation of 860these algorithms. 861 862 863Encryption and Decryption 864------------------------- 865 866On Android and Java it's easy to encrypt streams. 867 868Callers are responsible for the initialization of the encryption or decryption cipher with the 869chosen algorithm, the key, and algorithm-specific additional parameters like the initialization 870vector. The following example shows a typical usage with AES encryption, in which `key` and `iv` 871parameters should both be 16 bytes long. 872 873=== "Java" 874 875 Use `Okio.cipherSink(Sink, Cipher)` or `Okio.cipherSource(Source, Cipher)` to encrypt or decrypt 876 a stream using a block cipher. 877 878 ```java 879 void encryptAes(ByteString bytes, Path path, byte[] key, byte[] iv) 880 throws GeneralSecurityException, IOException { 881 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 882 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); 883 try (BufferedSink sink = Okio.buffer( 884 Okio.cipherSink(FileSystem.SYSTEM.sink(path), cipher))) { 885 sink.write(bytes); 886 } 887 } 888 889 ByteString decryptAesToByteString(Path path, byte[] key, byte[] iv) 890 throws GeneralSecurityException, IOException { 891 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 892 cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); 893 try (BufferedSource source = Okio.buffer( 894 Okio.cipherSource(FileSystem.SYSTEM.source(path), cipher))) { 895 return source.readByteString(); 896 } 897 } 898 ``` 899 900=== "Kotlin" 901 902 Encryption and decryption functions are extensions on `Cipher`: 903 904 ```kotlin 905 fun encryptAes(bytes: ByteString, path: Path, key: ByteArray, iv: ByteArray) { 906 val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") 907 cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) 908 val cipherSink = FileSystem.SYSTEM.sink(path).cipherSink(cipher) 909 cipherSink.buffer().use { 910 it.write(bytes) 911 } 912 } 913 914 fun decryptAesToByteString(path: Path, key: ByteArray, iv: ByteArray): ByteString { 915 val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") 916 cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) 917 val cipherSource = FileSystem.SYSTEM.source(path).cipherSource(cipher) 918 return cipherSource.buffer().use { 919 it.readByteString() 920 } 921 } 922 ``` 923 924 925[base64]: https://tools.ietf.org/html/rfc4648#section-4 926[bmp]: https://en.wikipedia.org/wiki/BMP_file_format 927[nfd]: https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.Form.html#NFD 928[nfc]: https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.Form.html#NFC 929[BitmapEncoderKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/BitmapEncoder.kt 930[BitmapEncoder]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/BitmapEncoder.java 931[ExploreCharsetsKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ExploreCharsets.kt 932[ExploreCharsets]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ExploreCharsets.java 933[GoldenValueKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/GoldenValue.kt 934[GoldenValue]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/GoldenValue.java 935[HashingKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/Hashing.kt 936[Hashing]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/Hashing.java 937[ReadFileLineByLine]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/ReadFileLineByLine.java 938[ReadFileLineByLineKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/ReadFileLineByLine.kt 939[SocksProxyServerKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/SocksProxyServer.kt 940[SocksProxyServer]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/SocksProxyServer.java 941[WriteFile]: https://github.com/square/okio/blob/master/samples/src/jvmMain/java/okio/samples/WriteFile.java 942[WriteFileKt]: https://github.com/square/okio/blob/master/samples/src/jvmMain/kotlin/okio/samples/WriteFile.kt 943