package client import ( "bytes" "errors" "fmt" "io" "sync" ) // ErrStreamOverflow is returned by streamBuffer.Read once the buffer has // exceeded its limit. The tunnel is unusable at that point and must be torn // down; the alternative would be growing without bound. var ErrStreamOverflow = errors.New("tunnel buffer overflow") // defaultStreamLimit caps how much unread tunnel data we hold. // // USB/IP traffic is request/response, so the consumer normally keeps up. A // backlog this large means the USB side has stalled, and 8 MB is far more // than any legitimate burst of in-flight URBs. const defaultStreamLimit = 8 << 20 // streamBuffer is an unbounded-write, blocking-read byte pipe. // // It replaces io.Pipe on the path from the WebSocket read loop into the // USB/IP server. io.Pipe is synchronous: a Write blocks until a Reader has // consumed the bytes, so feeding it from the WebSocket read loop meant one // slow USB transfer froze the entire client — no control messages, no // keepalives, no other tunnel. Writes here never block. type streamBuffer struct { mu sync.Mutex cond *sync.Cond buf bytes.Buffer limit int closed bool err error } func newStreamBuffer() *streamBuffer { return newStreamBufferLimit(defaultStreamLimit) } func newStreamBufferLimit(limit int) *streamBuffer { s := &streamBuffer{limit: limit} s.cond = sync.NewCond(&s.mu) return s } // Write appends data to the buffer and never blocks. // Once the limit is exceeded the stream is failed: further reads drain what // is already buffered and then return ErrStreamOverflow. func (s *streamBuffer) Write(p []byte) (int, error) { s.mu.Lock() defer s.mu.Unlock() if s.closed { return 0, io.ErrClosedPipe } if s.err != nil { return 0, s.err } if s.buf.Len()+len(p) > s.limit { s.err = fmt.Errorf("%w: %d bytes buffered, limit %d", ErrStreamOverflow, s.buf.Len(), s.limit) s.cond.Broadcast() return 0, s.err } n, err := s.buf.Write(p) s.cond.Broadcast() return n, err } // Read blocks until data is available, the stream is closed, or it failed. func (s *streamBuffer) Read(p []byte) (int, error) { s.mu.Lock() defer s.mu.Unlock() for s.buf.Len() == 0 { if s.err != nil { return 0, s.err } if s.closed { return 0, io.EOF } s.cond.Wait() } return s.buf.Read(p) } // Close makes pending and future reads return EOF once the buffer is drained. func (s *streamBuffer) Close() error { s.mu.Lock() defer s.mu.Unlock() s.closed = true s.cond.Broadcast() return nil } // Buffered reports how many bytes are waiting to be read. func (s *streamBuffer) Buffered() int { s.mu.Lock() defer s.mu.Unlock() return s.buf.Len() }