github.com/glycerine/xcryptossh@v7.0.4+incompatible/writebump_test.go (about)

     1  package ssh
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  // Given a read-only idle timeout of 1 sec, and a write happening
    11  // every 100 msec: when reads stop, even with the ongoing write
    12  // success, the read should still timeout. i.e. read timeout should
    13  // be independent of write success. This is needed because
    14  // writers are buffered and typically return a nil error, but
    15  // this tells us nothing about the status of connectivity.
    16  func TestTimeout009ReadsIdleOutEvenIfWritesOK(t *testing.T) {
    17  	defer xtestend(xtestbegin(t))
    18  
    19  	halt := NewHalter()
    20  	defer halt.RequestStop()
    21  
    22  	r, wun, mux := channelPair(t, halt)
    23  	defer wun.Close()
    24  
    25  	writeFreq := time.Millisecond * 100
    26  
    27  	idleout := 1000 * time.Millisecond
    28  	overall := 3 * idleout
    29  
    30  	t0 := time.Now()
    31  	tstop := t0.Add(overall)
    32  
    33  	tExpectIdleOut := t0.Add(idleout)
    34  
    35  	// set the timeout on the reader
    36  	err := r.SetReadIdleTimeout(idleout)
    37  	if err != nil {
    38  		panic(fmt.Sprintf("r.SetIdleTimeout: %v", err))
    39  	}
    40  
    41  	readErr := make(chan error)
    42  	writeErr := make(chan error)
    43  	var ring *infiniteRing
    44  
    45  	go to009ReaderToRing(idleout, r, overall, tstop, readErr, &ring)
    46  
    47  	go to009pingWrite(r, tstop, writeFreq, overall, writeErr, halt)
    48  
    49  	// wait for our overall time, and for both to return
    50  	var rerr, werr error
    51  	var rok, wok bool
    52  	complete := func() bool {
    53  		return rok && wok
    54  	}
    55  collectionLoop:
    56  	for {
    57  		select {
    58  		case <-time.After(2 * overall):
    59  			panic(fmt.Sprintf("TestTimeout009WriteIdlesOutWhenReadsStop: waited " +
    60  				"two overall, yet still no idle timeout!"))
    61  
    62  		case rerr = <-readErr:
    63  			p("got rerr: '%#v'", rerr)
    64  			now := time.Now()
    65  
    66  			// the main point of the test is these checks:
    67  
    68  			// we want tExpectIdleOut >=  now  >=  tstop
    69  			// so that the read idled-out even though writes are ok
    70  			if now.Before(tExpectIdleOut) {
    71  				panic(fmt.Sprintf("rerr: '%v', stopped too early, before '%v'. now=%v. now-expected=%v", rerr, tExpectIdleOut, now, now.Sub(tExpectIdleOut)))
    72  			}
    73  			if now.After(tstop) {
    74  				panic(fmt.Sprintf("rerr: '%v', stopped too late, after '%v'. now=%v. now-tstop=%v", rerr, tstop, now, now.Sub(tstop)))
    75  			}
    76  			rok = true
    77  
    78  			if complete() {
    79  				break collectionLoop
    80  			}
    81  
    82  		case werr = <-writeErr:
    83  			p("got werr")
    84  			now := time.Now()
    85  			if now.Before(tstop) {
    86  				panic(fmt.Sprintf("werr: '%v', stopped too early, before '%v'. now=%v. now-before=%v", werr, tstop, now, now.Sub(tstop)))
    87  			}
    88  			wok = true
    89  
    90  			if complete() {
    91  				break collectionLoop
    92  			}
    93  		}
    94  
    95  	}
    96  	p("done with collection loop")
    97  
    98  	r.Close()
    99  	mux.Close()
   100  
   101  }
   102  
   103  // setup reader r -> infiniteRing ring. returns
   104  // readOk upon success.
   105  func to009ReaderToRing(idleout time.Duration, r Channel, overall time.Duration, tstop time.Time, readErr chan error, pRing **infiniteRing) (err error) {
   106  	defer func() {
   107  		p("readerToRing returning on readErr, err = '%v'", err)
   108  		readErr <- err
   109  	}()
   110  
   111  	ring := newInfiniteRing()
   112  	*pRing = ring
   113  
   114  	src := r
   115  	dst := ring
   116  	buf := make([]byte, 32*1024)
   117  
   118  	for {
   119  		nr, er := src.Read(buf)
   120  		if nr > 0 {
   121  			nw, ew := dst.Write(buf[0:nr])
   122  			if ew != nil {
   123  				err = ew
   124  				p("readerToRing sees Write err %v", ew)
   125  				break
   126  			}
   127  			if nr != nw {
   128  				err = io.ErrShortWrite
   129  				break
   130  			}
   131  		}
   132  
   133  		if time.Now().After(tstop) {
   134  			p("reader: reached tstop, bailing out of copy loop.")
   135  			return readOk
   136  		}
   137  
   138  		if er != nil {
   139  			p("readerToRing sees Read err %v", er)
   140  			if er != io.EOF {
   141  				err = er
   142  			}
   143  			break
   144  		}
   145  	} //end for
   146  
   147  	return err
   148  }
   149  
   150  // write a zero byte every freq, so keep idling from idling out.
   151  // we return after overall
   152  func to009pingWrite(w Channel, tstop time.Time, writeFreq time.Duration, overall time.Duration, writeErr chan error, halt *Halter) (err error) {
   153  	defer func() {
   154  		halt.MarkDone()
   155  		p("readerToRing returning on readErr, err = '%v'", err)
   156  		writeErr <- err
   157  	}()
   158  
   159  	buf := make([]byte, 1)
   160  	ping := time.After(writeFreq)
   161  	overallTime := time.After(overall)
   162  	for {
   163  		select {
   164  		case <-ping:
   165  			// byte a byte
   166  			w.Write(buf)
   167  			ping = time.After(writeFreq)
   168  
   169  		case <-halt.ReqStopChan():
   170  			return
   171  		case <-overallTime:
   172  			return
   173  		}
   174  	}
   175  }