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  }