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  }