github.com/glycerine/xcryptossh@v7.0.4+incompatible/cts_test.go (about) 1 package ssh 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "testing" 8 "time" 9 10 "github.com/glycerine/rbuf" 11 ) 12 13 var timeGood = fmt.Errorf("overall time completed") 14 var writeOk = fmt.Errorf("write was ok") 15 var readOk = fmt.Errorf("read was ok") 16 17 // a simple circular buffer than 18 // we can fill for any amount of 19 // time and track the total number 20 // of bytes written to it. 21 type infiniteRing struct { 22 ring *rbuf.FixedSizeRingBuf 23 nrtot int64 24 nwtot int64 25 next int64 26 sz int 27 } 28 29 const ringsz = 64 * 1024 30 const maxwords = ringsz / 8 31 32 func newInfiniteRing() *infiniteRing { 33 return &infiniteRing{ 34 ring: rbuf.NewFixedSizeRingBuf(ringsz), 35 sz: ringsz, 36 } 37 } 38 39 // Write checks and panics if data is not in order. 40 // It expects each 64-bit word to contain the next 41 // integer, little endian. 42 func (ir *infiniteRing) Write(b []byte) (n int, err error) { 43 words := len(b) / 8 44 if words > maxwords { 45 words = maxwords 46 } 47 if words == 0 { 48 return 0, nil 49 } 50 ir.ring.Reset() 51 n, err = ir.ring.WriteAndMaybeOverwriteOldestData(b[:words*8]) 52 ir.nwtot += int64(n) 53 q("infiniteRing.Write total of %v", ir.nwtot) 54 55 expect := make([]byte, 8) 56 by := ir.ring.Bytes() 57 for i := 0; i < words; i++ { 58 binary.LittleEndian.PutUint64(expect, uint64(ir.next)) 59 obs := by[i*8 : (i+1)*8] 60 obsnum := int64(binary.LittleEndian.Uint64(obs)) 61 if obsnum != ir.next { 62 panic(fmt.Sprintf("bytes written to ring where not in order! observed='%v', expected='%v'. at i=%v out of %v words", obsnum, ir.next, i, words)) 63 } 64 ir.next++ 65 } 66 return 67 } 68 69 func (ir *infiniteRing) Read(b []byte) (n int, err error) { 70 n, err = ir.ring.Read(b) 71 ir.nrtot += int64(n) 72 return 73 } 74 75 type seqWords struct { 76 next int64 77 } 78 79 // provide the integers, starting at zero and 80 // counting up, as 64-bit words. 81 func newSequentialWords() *seqWords { 82 return &seqWords{} 83 } 84 85 func (s *seqWords) Read(b []byte) (n int, err error) { 86 numword := len(b) / 8 87 for i := 0; i < numword; i++ { 88 binary.LittleEndian.PutUint64(b[i*8:(i+1)*8], uint64(s.next)) 89 s.next++ 90 } 91 //p("seqWords.Read up to %v done, total bytes %v", s.next, s.next*8) 92 return numword * 8, nil 93 } 94 95 // Given a 2 sec idle *read* or *write* timeout, if we continuously transfer 96 // for 20 seconds (or 10x our idle timeout), we should not see any timeout since 97 // our activity is ongoing continuously. 98 func TestCtsReadWithNoIdleTimeout(t *testing.T) { 99 defer xtestend(xtestbegin(t)) 100 testCts(true, t) 101 } 102 func TestCtsWriteWithNoIdleTimeout(t *testing.T) { 103 defer xtestend(xtestbegin(t)) 104 testCts(false, t) 105 } 106 107 func setTo(r, w Channel, timeOutOnReader bool, idleout time.Duration) { 108 // set the timeout on the reader/writer 109 if timeOutOnReader { 110 err := r.SetReadIdleTimeout(idleout) 111 if err != nil { 112 panic(fmt.Sprintf("r.SetIdleTimeout: %v", err)) 113 } 114 } else { 115 // set the timeout on the writer 116 err := w.SetWriteIdleTimeout(idleout) 117 if err != nil { 118 panic(fmt.Sprintf("w.SetIdleTimeout: %v", err)) 119 } 120 } 121 } 122 123 func setClose(r, w Channel, closeReader bool) { 124 // set the timeout on the writer, ignore 125 // errors, probably race to shutdown; this is 126 // aimed at shutdown. 127 if closeReader { 128 r.Close() 129 } else { 130 w.Close() 131 } 132 } 133 134 func testCts(timeOutOnReader bool, t *testing.T) { 135 halt := NewHalter() 136 defer halt.RequestStop() 137 138 r, w, mux := channelPair(t, halt) 139 140 p("r.idleTimer = %p", r.idleR) 141 p("w.idleTimer = %p", w.idleW) 142 143 idleout := 2000 * time.Millisecond 144 overall := 10 * idleout 145 146 t0 := time.Now() 147 tstop := t0.Add(overall) 148 149 haltr := NewHalter() 150 haltw := NewHalter() 151 defer haltr.RequestStop() 152 defer haltw.RequestStop() 153 154 setTo(r, w, timeOutOnReader, idleout) 155 readErr := make(chan error) 156 writeErr := make(chan error) 157 var seq *seqWords 158 var ring *infiniteRing 159 160 go readerToRing(idleout, r, haltr, overall, tstop, readErr, &ring) 161 162 go seqWordsToWriter(w, haltw, tstop, writeErr, &seq) 163 164 after := time.After(overall) 165 166 // wait for our overall time, and for both to return 167 var rerr, werr error 168 var rok, wok bool 169 var haltrDone, haltwDone bool 170 complete := func() bool { 171 return rok && wok && haltrDone && haltwDone 172 } 173 collectionLoop: 174 for { 175 select { 176 case <-haltr.DoneChan(): 177 haltrDone = true 178 if complete() { 179 break collectionLoop 180 } 181 case <-haltw.DoneChan(): 182 haltwDone = true 183 if complete() { 184 break collectionLoop 185 } 186 case <-after: 187 p("after completed!") 188 189 after = nil 190 191 // the main point of the test: did after timeout 192 // fire before r or w returned? 193 if rok || wok { 194 panic("sadness, failed test: rok || wok happened before overall elapsed") 195 } else { 196 p("success!!!!!") 197 } 198 199 // release the other. e.g. the writer will typically be blocked after 200 // the reader timeout test, since the writer didn't get a timeout. 201 // Closing is faster than setting a timeout and waiting for it. 202 setClose(r, w, !timeOutOnReader) 203 204 haltr.RequestStop() 205 haltw.RequestStop() 206 207 if complete() { 208 break collectionLoop 209 } 210 211 case rerr = <-readErr: 212 p("got rerr") 213 now := time.Now() 214 if now.Before(tstop) { 215 panic(fmt.Sprintf("rerr: '%v', stopped too early, before '%v'. now=%v. now-before=%v", rerr, tstop, now, now.Sub(tstop))) // panicing here 216 } 217 rok = true 218 if complete() { 219 break collectionLoop 220 } 221 222 case werr = <-writeErr: 223 p("got werr") 224 now := time.Now() 225 if now.Before(tstop) { 226 panic(fmt.Sprintf("rerr: '%v', stopped too early, before '%v'. now=%v. now-before=%v", werr, tstop, now, now.Sub(tstop))) 227 } 228 wok = true 229 if complete() { 230 break collectionLoop 231 } 232 } 233 234 } 235 p("done with collection loop") 236 237 // sanity check that we read all we wrote. 238 seqby := (seq.next - 1) * 8 239 if ring.nwtot != seqby { 240 // panic: wrote 18636636160 but read 18636799992. diff=-163832 241 // the differ by some, since shutdown isn't coordinated 242 // by having the sender stop sending and close first. 243 p("wrote %v but read %v. diff=%v", ring.nwtot, seqby, ring.nwtot-seqby) 244 } 245 246 // actually shutdown is pretty racy, lots of possible errors on Close, 247 // such as EOF 248 w.Close() 249 r.Close() 250 mux.Close() 251 252 } 253 254 // setup reader r -> infiniteRing ring. returns 255 // readOk upon success. 256 func readerToRing(idleout time.Duration, r Channel, halt *Halter, overall time.Duration, tstop time.Time, readErr chan error, pRing **infiniteRing) (err error) { 257 defer func() { 258 p("readerToRing returning on readErr, err = '%v'", err) 259 readErr <- err 260 halt.MarkDone() 261 }() 262 263 ring := newInfiniteRing() 264 *pRing = ring 265 266 src := r 267 dst := ring 268 buf := make([]byte, 32*1024) 269 numwrites := 0 270 for { 271 nr, er := src.Read(buf) 272 if nr > 0 { 273 nw, ew := dst.Write(buf[0:nr]) 274 if ew != nil { 275 err = ew 276 p("readerToRing sees Write err %v", ew) 277 break 278 } 279 if nr != nw { 280 err = io.ErrShortWrite 281 break 282 } 283 numwrites++ 284 select { 285 case <-halt.ReqStopChan(): 286 return readOk 287 default: 288 } 289 } 290 if er != nil { 291 p("readerToRing sees Read err %v", er) 292 if er != io.EOF { 293 err = er 294 } 295 break 296 } 297 } //end for 298 299 return err 300 } 301 302 // read from the integers 0,1,2,... and write to w until tstop. 303 // returns writeOk upon success 304 func seqWordsToWriter(w Channel, halt *Halter, tstop time.Time, writeErr chan error, pSeqWords **seqWords) (err error) { 305 defer func() { 306 //p("seqWordsToWriter returning err = '%v'", err) 307 writeErr <- err 308 halt.MarkDone() 309 }() 310 src := newSequentialWords() 311 *pSeqWords = src 312 dst := w 313 buf := make([]byte, 32*1024) 314 for { 315 nr, er := src.Read(buf) 316 if nr > 0 { 317 nw, ew := dst.Write(buf[0:nr]) 318 if ew != nil { 319 //p("seqWriter sees Write err %v", ew) 320 err = ew 321 break 322 } 323 if nr != nw { 324 err = io.ErrShortWrite 325 break 326 } 327 select { 328 case <-halt.ReqStopChan(): 329 return writeOk 330 default: 331 } 332 } 333 if er != nil { 334 p("seqWriter sees Read err %v", er) 335 if er != io.EOF { 336 err = er 337 } 338 break 339 } 340 } 341 342 return err 343 }