func (r *NBReader) readWithTimeout(buffer bytes.Buffer, timeout time.Duration) (int, error) {
	select {
	case data, ok := <-r.dataChan:
		r.buffer.Write(data)
		if !ok {
			return len(data), io.EOF
		}
		return len(data), nil
	case <-time.After(timeout):
		return 0, errTimeout
	}
}
readWithTimeout consumes the data channel filled by readInput() and respects the set timeouts
package nbreader
import (
	"bytes"
	"errors"
	"io"
	"time"
)
var errTimeout = errors.New("timeout")
type NBReader struct {
	blockSize    int
	reader       io.Reader
	dataChan     chan []byte
	buffer       bytes.Buffer
	chunkTimeout time.Duration
	forceTimeout time.Duration
	isEOF        bool
}
type Option func(r *NBReader)
func ChunkTimeout(duration time.Duration) Option {
	return func(r *NBReader) {
		r.chunkTimeout = duration
	}
}
func Timeout(duration time.Duration) Option {
	return func(r *NBReader) {
		r.forceTimeout = duration
	}
}
func NewNBReader(reader io.Reader, blockSize int, options ...Option) *NBReader {
	dataChan := make(chan []byte)
	r := NBReader{reader: reader, dataChan: dataChan, blockSize: blockSize}
	for _, option := range options {
		option(&r)
	}
	go r.readInput()
	return &r
}
func (r *NBReader) Read(buffer []byte) (int, error) {
	var (
		remaining   time.Duration
		nextTimeout time.Duration
		start       = time.Now()
		lastStart   = time.Now()
	)
	if len(buffer) <= r.buffer.Len() {
		ret, _ := r.buffer.Read(buffer)
		return ret, nil
	}
	if r.isEOF {
		return r.buffer.Read(buffer)
	}
	for r.buffer.Len() < len(buffer) {
		lastStart = time.Now()
		remaining = r.forceTimeout - time.Now().Sub(start)
		if r.chunkTimeout == 0 || r.chunkTimeout > remaining {
			nextTimeout = remaining
		} else {
			nextTimeout = r.chunkTimeout
		}
		_, err := r.readWithTimeout(r.buffer, nextTimeout)
		duration := time.Now().Sub(lastStart)
		if err == errTimeout {
			if duration >= r.chunkTimeout {
				break
			}
		}
		if err == io.EOF {
			r.isEOF = true
			break
		}
		if time.Now().Sub(start) >= r.forceTimeout {
			break
		}
	}
	ret, _ := r.buffer.Read(buffer)
	return ret, nil
}
func (r *NBReader) readInput() {
	for {
		tmp := make([]byte, r.blockSize)
		length, err := r.reader.Read(tmp)
		if err != nil {
			break
		}
		r.dataChan <- tmp[0:length]
	}
	close(r.dataChan)
}