github.com/glycerine/xcryptossh@v7.0.4+incompatible/readto_test.go (about) 1 package ssh 2 3 import ( 4 "fmt" 5 "io" 6 "net" 7 "sync/atomic" 8 "testing" 9 "time" 10 ) 11 12 // Given a 1000 msec idle read timeout, when writes stop, the Read() calls 13 // should return Timeout() true errors. This is the compliment to 14 // the writeto_test.go. 15 // 16 func TestTimeout008ReadIdlesOutWhenWriteStops(t *testing.T) { 17 defer xtestend(xtestbegin(t)) 18 19 halt := NewHalter() 20 defer halt.RequestStop() 21 22 r, w, mux := channelPair(t, halt) 23 24 idleout := 2 * time.Second // tried 1 sec, but bogged down machine still gave false pos. 25 overall := 4 * idleout 26 27 t0 := time.Now() 28 tstop := t0.Add(overall) 29 30 // set the timeout on the reader 31 err := r.SetReadIdleTimeout(idleout) 32 if err != nil { 33 panic(fmt.Sprintf("r.SetIdleTimeout: %v", err)) 34 } 35 36 readErr := make(chan error) 37 writeErr := make(chan error) 38 var seq *seqWords 39 var ring *infiniteRing 40 var whenLastReadTimedout time.Time 41 var lastread int64 42 43 go to008ReaderToRing(idleout, r, overall, tstop, readErr, &ring, &whenLastReadTimedout, &lastread) 44 45 go to008SeqWordsToWriter(w, tstop, writeErr, &seq) 46 47 var rerr, werr error 48 var rok, wok bool 49 complete := func() bool { 50 return rok && wok 51 } 52 collectionLoop: 53 for { 54 select { 55 case <-time.After(overall + 3*idleout): 56 // on slow, bogged down systems, we may still be reading 57 // just fine, and still timeout, due to the reads arriving 58 // fine, but just taking a while. So check that and continue 59 // if our most recent read was quite recent. 60 mnow := monoNow() 61 myLastread := atomic.LoadInt64(&lastread) 62 if mnow-myLastread < int64(idleout) { 63 continue 64 } 65 66 panic(fmt.Sprintf("TestTimeout008ReadIdlesOutWhenWriteStops deadlocked: went past 3x overall")) 67 68 case rerr = <-readErr: 69 p("got rerr: '%#v'", rerr) 70 now := time.Now() 71 if now.Before(tstop) { 72 panic(fmt.Sprintf("rerr: '%v', stopped too early, before '%v'. now=%v. now-before=%v", rerr, tstop, now, now.Sub(tstop))) // panicing here 73 } 74 rok = true 75 76 // verify that read got a timeout: this is the main point of this test. 77 nerr, ok := rerr.(net.Error) 78 if !ok || !nerr.Timeout() { 79 panic(fmt.Sprintf("big problem: expected a timeout error back from Read()."+ 80 " instead got '%v'", rerr)) 81 } 82 83 if complete() { 84 break collectionLoop 85 } 86 87 case werr = <-writeErr: 88 p("got werr") 89 now := time.Now() 90 if now.Before(tstop) { 91 panic(fmt.Sprintf("rerr: '%v', stopped too early, before '%v'. now=%v. now-before=%v", werr, tstop, now, now.Sub(tstop))) 92 } 93 wok = true 94 if complete() { 95 break collectionLoop 96 } 97 } 98 99 } 100 p("done with collection loop") 101 102 p("whenLastReadTimedout=%v, tstop=%v, idleout=%v", whenLastReadTimedout, tstop, idleout) 103 104 // sanity check that whenLastReadTimedout in when we expect 105 if whenLastReadTimedout.Before(tstop) { 106 panic("premature timeout, very bad") 107 } 108 109 w.Close() 110 r.Close() 111 mux.Close() 112 113 } 114 115 // setup reader r -> infiniteRing ring. 116 func to008ReaderToRing(idleout time.Duration, r Channel, overall time.Duration, tstop time.Time, readErr chan error, pRing **infiniteRing, whenerr *time.Time, lastread *int64) (err error) { 117 defer func() { 118 p("readerToRing returning on readErr, err = '%v'", err) 119 readErr <- err 120 }() 121 122 ring := newInfiniteRing() 123 *pRing = ring 124 125 src := r 126 dst := ring 127 buf := make([]byte, 32*1024) 128 129 for { 130 p("readto_test: readerToRing calling read.") 131 nr, er := src.Read(buf) 132 p("readto_test: readerToRing back from read. err='%v'. nr=%v", er, nr) 133 *whenerr = time.Now() 134 if nr > 0 { 135 atomic.StoreInt64(lastread, monoNow()) 136 nw, ew := dst.Write(buf[0:nr]) 137 if ew != nil { 138 err = ew 139 p("readerToRing sees Write err %v", ew) 140 break 141 } 142 if nr != nw { 143 err = io.ErrShortWrite 144 break 145 } 146 } 147 if er != nil { 148 p("readerToRing sees Read err %v", er) 149 if er != io.EOF { 150 err = er 151 } 152 break 153 } 154 } //end for 155 156 return err 157 } 158 159 // read from the integers 0,1,2,... and write to w until tstop. 160 // returns writeOk upon success 161 func to008SeqWordsToWriter(w Channel, tstop time.Time, writeErr chan error, pSeqWords **seqWords) (err error) { 162 defer func() { 163 p("seqWordsToWriter returning err = '%v'", err) 164 writeErr <- err 165 }() 166 src := newSequentialWords() 167 *pSeqWords = src 168 dst := w 169 buf := make([]byte, 32*1024) 170 for { 171 nr, er := src.Read(buf) 172 if nr > 0 { 173 nw, ew := dst.Write(buf[0:nr]) 174 if ew != nil { 175 //p("seqWriter sees Write err %v", ew) 176 err = ew 177 break 178 } 179 if nr != nw { 180 err = io.ErrShortWrite 181 break 182 } 183 if time.Now().After(tstop) { 184 p("to008SeqWordsToWriter: reached tstop, bailing out of copy loop.") 185 return writeOk 186 } 187 } 188 if er != nil { 189 p("seqWriter sees Read err %v", er) 190 if er != io.EOF { 191 err = er 192 } 193 break 194 } 195 } 196 197 return err 198 }