github.com/karrick/gorill@v1.10.3/README.md (about) 1 # gorill 2 3 Small Go library for various stream wrappers. A 'rill' is a small stream. 4 5 ### Usage 6 7 Documentation is available via 8 [](https://godoc.org/github.com/karrick/gorill). 9 10 ### Description 11 12 One of the strengths of Go's interface system is that it allows easy composability of data types. 13 An `io.Writer` is any data structure that exposes the `Write([]byte) (int,error)` method. 14 15 If a program has an `io.Writer` but requires one that buffers its output somewhat, the program could 16 use the Go standard library `bufio.Writer` to wrap the original `io.Writer`, providing buffering on 17 the data writes. That works great, but the programmer must be willing to allow the buffer to 18 completely fill before flushing data to the final output stream. Instead, the program could use 19 `gorill.SpooledWriteCloser` which buffers writes, but flushes data at a configurable periodicity. 20 21 ### Supported Use cases 22 23 ##### FilesReader 24 25 FilesReader is an io.ReadCloser that can be used to read over the contents of all of the files 26 specified by pathnames. It only opens a single file handle at a time. When reading from the 27 currently open file handle returns io.EOF, it closes that file handle, and the next Read will cause 28 the following file in the series to be opened and read from. Similar to io.MultiReader. 29 30 ```Go 31 var ior io.Reader 32 if flag.NArg() == 0 { 33 ior = os.Stdin 34 } else { 35 ior = &gorill.FilesReader{Pathnames: flag.Args()} 36 } 37 38 lines := bufio.NewScanner(ior) 39 for lines.Scan() { 40 // ... 41 } 42 ``` 43 44 ##### LineTerminatedReader 45 46 When a program has an `io.Reader` and needs to ensure the final byte 47 read is a newline, the `gorill.LineTerminatedReader` may be used to 48 wrap the source `io.Reader` to provide that assurance. 49 50 ```Go 51 func ExampleNewLineTerminatedReader() { 52 r := &LineTerminatedReader{R: bytes.NewReader([]byte("123\n456"))} 53 buf, err := ioutil.ReadAll(r) 54 if err != nil { 55 fmt.Fprintf(os.Stderr, "%s\n", err) 56 os.Exit(1) 57 } 58 if got, want := len(buf), 8; got != want { 59 fmt.Fprintf(os.Stderr, "GOT: %v; WANT: %v\n", got, want) 60 os.Exit(1) 61 } 62 fmt.Printf("%q\n", buf[len(buf)-1]) 63 // Output: '\n' 64 } 65 ``` 66 67 ##### NopWriteCloser 68 69 If a program has an `io.Writer` but requires an `io.WriteCloser`, the program can imbue the 70 `io.Writer` with a no-op `Close` method. The resultant structure can be used anywhere an 71 `io.WriteCloser` is required. 72 73 ```Go 74 iowc := gorill.NopCloseWriter(iow) 75 iowc.Close() // does nothing 76 ``` 77 78 Alternatively, if you already have an `io.WriteCloser`, but you want its `Close` method to do 79 nothing, then wrap it in a `NopCloseWriter`. 80 81 ##### NopReadCloser 82 83 If a program has an `io.Reader` but requires an `io.ReadCloser`, the program can imbue the 84 `io.Reader` with a no-op `Close` method. The resultant structure can be used anywhere an 85 `io.ReadCloser` is required. The Go standard library provides this exact functionality by the 86 `ioutil.NopCloser(io.Reader) io.ReadCloser` function. It is also provided by this library for 87 symmetry with the `gorill.NopCloseWriter` call above. 88 89 ```Go 90 iorc := gorill.NopCloseReader(ior) 91 iorc.Close() // does nothing 92 ``` 93 94 Alternatively, if you already have an `io.ReadCloser`, but you want its `Close` method to do 95 nothing, then wrap it in a `gorill.NopCloseReader`. 96 97 ##### SpooledWriteCloser 98 99 If a program has an `io.WriteCloser` but requires one that spools its data over perhaps a slow 100 network connection, the program can use a `gorill.SpooledWriteCloser` to wrap the original 101 `io.WriteCloser`, but ensure the data is flushed periodically. 102 103 ```Go 104 // uses gorill.DefaultFlushPeriod and gorill.DefaultBufSize 105 spooler, err := gorill.NewSpooledWriteCloser(iowc) 106 if err != nil { 107 return err 108 } 109 ``` 110 111 You can customize either or both the size of the underlying buffer, and the frequency of buffer 112 flushes, based on your program's requirements. Simply list the required customizations after the 113 underlying `io.WriteCloser`. 114 115 ```Go 116 spooler, err := gorill.NewSpooledWriteCloser(iowc, gorill.BufSize(8192), gorill.Flush(time.Second)) 117 if err != nil { 118 return err 119 } 120 ``` 121 122 If the program has an `io.Writer` but needs a spooled writer, it can compose data structures to 123 achieve the required functionality: 124 125 ```Go 126 spooler, err := gorill.NewSpooledWriteCloser(gorill.NopCloseWriter(iow), gorill.Flush(time.Second)) 127 if err != nil { 128 return err 129 } 130 ``` 131 132 ##### TimedReadCloser 133 134 If a program has an `io.Reader` but requires one that has a built in timeout for reads, one can 135 wrap the original `io.Reader`, but modify the `Read` method to provide the required timeout 136 handling. The new data structure can be used anywhere the original `io.Reader` was used, and 137 seemlessly handles reads that take too long. 138 139 ```Go 140 timed := gorill.NewTimedReadCloser(iowc, 10*time.Second) 141 buf := make([]byte, 1000) 142 n, err := timed.Read(buf) 143 if err != nil { 144 if terr, ok := err.(gorill.ErrTimeout); ok { 145 // timeout occurred 146 } 147 return err 148 } 149 ``` 150 151 ##### TimedWriteCloser 152 153 If a program has an `io.Writer` but requires one that has a built in timeout for writes, one can 154 wrap the original `io.Writer`, but modify the `Write` method to provide the required timeout 155 handling. The new data structure can be used anywhere the original `io.Writer` was used, and 156 seemlessly handles writes that take too long. 157 158 ```Go 159 timed := gorill.NewTimedWriteCloser(iowc, 10*time.Second) 160 n, err := timed.Write([]byte("example")) 161 if err != nil { 162 if terr, ok := err.(gorill.ErrTimeout); ok { 163 // timeout occurred 164 } 165 return err 166 } 167 ``` 168 169 ## LockingWriteCloser 170 171 If a program needs an `io.WriteCloser` that can be concurrently used by more than one go-routine, it 172 can use a `gorill.LockingWriteCloser`. Benchmarks show a 3x performance gain by using `sync.Mutex` 173 rather than channels for this case. `gorill.LockingWriteCloser` data structures provide this 174 peformance benefit. 175 176 ```Go 177 lwc := gorill.NewLockingWriteCloser(os.Stdout) 178 for i := 0; i < 1000; i++ { 179 go func(iow io.Writer, i int) { 180 for j := 0; j < 100; j++ { 181 _, err := iow.Write([]byte("Hello, World, from %d!\n", i)) 182 if err != nil { 183 return 184 } 185 } 186 }(lwc, i) 187 } 188 ``` 189 190 ##### MultiWriteCloserFanIn 191 192 If a program needs to be able to fan in writes from multiple `io.WriteCloser` instances to a single 193 `io.WriteCloser`, the program can use a `gorill.MultiWriteCloserFanIn`. 194 195 ```Go 196 func Example(largeBuf []byte) { 197 bb := gorill.NewNopCloseBuffer() 198 first := gorill.NewMultiWriteCloserFanIn(bb) 199 second := first.Add() 200 first.Write(largeBuf) 201 first.Close() 202 second.Write(largeBuf) 203 second.Close() 204 } 205 ``` 206 207 ##### MultiWriteCloserFanOut 208 209 If a program needs to be able to fan out writes to multiple `io.WriteCloser` instances, the program 210 can use a `gorill.MultiWriteCloserFanOut`. 211 212 ```Go 213 bb1 = gorill.NewNopCloseBuffer() 214 bb2 = gorill.NewNopCloseBuffer() 215 mw = gorill.NewMultiWriteCloserFanOut(bb1, bb2) 216 n, err := mw.Write([]byte("blob")) 217 if want := 4; n != want { 218 t.Errorf("Actual: %#v; Expected: %#v", n, want) 219 } 220 if err != nil { 221 t.Errorf("Actual: %#v; Expected: %#v", err, nil) 222 } 223 if want := "blob"; bb1.String() != want { 224 t.Errorf("Actual: %#v; Expected: %#v", bb1.String(), want) 225 } 226 if want := "blob"; bb2.String() != want { 227 t.Errorf("Actual: %#v; Expected: %#v", bb2.String(), want) 228 } 229 ``` 230 231 ### Supported Use cases for Testing 232 233 ##### NopCloseBuffer 234 235 If a test needs a `bytes.Buffer`, but one that has a `Close` method, the test could simply wrap the 236 `bytes.Buffer` structure with `ioutil.NopClose()`, but the resultant data structure would only 237 provide an `io.ReadCloser` interface, and not all the other convenient `bytes.Buffer` methods. 238 Instead the test could use `gorill.NopCloseBuffer` which simply imbues a no-op `Close` method to a 239 `bytes.Buffer` instance: 240 241 ```Go 242 func TestSomething(t *testing.T) { 243 bb := gorill.NopCloseBuffer() 244 bb.Write([]byte("example")) 245 bb.Close() // does nothing 246 } 247 ``` 248 249 Custom buffer sizes can also be used: 250 251 ```Go 252 func TestSomething(t *testing.T) { 253 bb := gorill.NopCloseBufferSize(16384) 254 bb.Write([]byte("example")) 255 bb.Close() // does nothing 256 } 257 ``` 258 259 ##### ShortWriter 260 261 If a test needs an `io.Writer` that simulates write errors, the test could wrap an existing 262 `io.Writer` with a `gorill.ShortWriter`. Writes to the resultant `io.Writer` will work as before, 263 unless the length of data to be written exceeds some preset limit. In this case, only the preset 264 limit number of bytes will be written to the underlying `io.Writer`, but the write will return this 265 limit and an `io.ErrShortWrite` error. 266 267 ```Go 268 func TestShortWrites(t *testing.T) { 269 bb := gorill.NopCloseBuffer() 270 sw := gorill.ShortWriter(bb, 16) 271 272 n, err := sw.Write([]byte("short write")) 273 // n == 11, err == nil 274 275 n, err := sw.Write([]byte("a somewhat longer write")) 276 // n == 16, err == io.ErrShortWrite 277 } 278 ``` 279 280 ##### SlowReader 281 282 If a test needs an `io.Reader` that writes all the data to the underlying `io.Reader`, but does so 283 after a delay, the test could wrap an existing `io.Reader` with a `gorill.SlowReader`. 284 285 ```Go 286 bb := gorill.NopCloseBuffer() 287 sr := gorill.SlowReader(bb, 10*time.Second) 288 289 buf := make([]byte, 1000) 290 n, err := sr.Read(buf) // this call takes at least 10 seconds to return 291 // n == 7, err == nil 292 ``` 293 294 ##### SlowWriter 295 296 If a test needs an `io.Writer` that writes all the data to the underlying `io.Writer`, but does so 297 after a delay, the test could wrap an existing `io.Writer` with a `gorill.SlowWriter`. 298 299 ```Go 300 func TestSlowWrites(t *testing.T) { 301 bb := gorill.NopCloseBuffer() 302 sw := gorill.SlowWriter(bb, 10*time.Second) 303 304 n, err := sw.Write([]byte("example")) // this call takes at least 10 seconds to return 305 // n == 7, err == nil 306 } 307 ```