258 lines
6.9 KiB
Go
258 lines
6.9 KiB
Go
package quic
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/handshake"
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
|
"github.com/lucas-clemente/quic-go/qerr"
|
|
)
|
|
|
|
type streamsMapLegacy struct {
|
|
mutex sync.RWMutex
|
|
|
|
perspective protocol.Perspective
|
|
|
|
streams map[protocol.StreamID]streamI
|
|
|
|
nextStreamToOpen protocol.StreamID // StreamID of the next Stream that will be returned by OpenStream()
|
|
highestStreamOpenedByPeer protocol.StreamID
|
|
nextStreamOrErrCond sync.Cond
|
|
openStreamOrErrCond sync.Cond
|
|
|
|
closeErr error
|
|
nextStreamToAccept protocol.StreamID
|
|
|
|
newStream newStreamLambda
|
|
|
|
numOutgoingStreams uint32
|
|
numIncomingStreams uint32
|
|
maxIncomingStreams uint32
|
|
maxOutgoingStreams uint32
|
|
}
|
|
|
|
var _ streamManager = &streamsMapLegacy{}
|
|
|
|
func newStreamsMapLegacy(newStream newStreamLambda, pers protocol.Perspective) streamManager {
|
|
// add some tolerance to the maximum incoming streams value
|
|
maxStreams := uint32(protocol.MaxIncomingStreams)
|
|
maxIncomingStreams := utils.MaxUint32(
|
|
maxStreams+protocol.MaxStreamsMinimumIncrement,
|
|
uint32(float64(maxStreams)*float64(protocol.MaxStreamsMultiplier)),
|
|
)
|
|
sm := streamsMapLegacy{
|
|
perspective: pers,
|
|
streams: make(map[protocol.StreamID]streamI),
|
|
newStream: newStream,
|
|
maxIncomingStreams: maxIncomingStreams,
|
|
}
|
|
sm.nextStreamOrErrCond.L = &sm.mutex
|
|
sm.openStreamOrErrCond.L = &sm.mutex
|
|
|
|
nextServerInitiatedStream := protocol.StreamID(2)
|
|
nextClientInitiatedStream := protocol.StreamID(3)
|
|
if pers == protocol.PerspectiveServer {
|
|
sm.highestStreamOpenedByPeer = 1
|
|
}
|
|
if pers == protocol.PerspectiveServer {
|
|
sm.nextStreamToOpen = nextServerInitiatedStream
|
|
sm.nextStreamToAccept = nextClientInitiatedStream
|
|
} else {
|
|
sm.nextStreamToOpen = nextClientInitiatedStream
|
|
sm.nextStreamToAccept = nextServerInitiatedStream
|
|
}
|
|
return &sm
|
|
}
|
|
|
|
// getStreamPerspective says which side should initiate a stream
|
|
func (m *streamsMapLegacy) streamInitiatedBy(id protocol.StreamID) protocol.Perspective {
|
|
if id%2 == 0 {
|
|
return protocol.PerspectiveServer
|
|
}
|
|
return protocol.PerspectiveClient
|
|
}
|
|
|
|
func (m *streamsMapLegacy) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) {
|
|
// every bidirectional stream is also a receive stream
|
|
return m.GetOrOpenStream(id)
|
|
}
|
|
|
|
func (m *streamsMapLegacy) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) {
|
|
// every bidirectional stream is also a send stream
|
|
return m.GetOrOpenStream(id)
|
|
}
|
|
|
|
// GetOrOpenStream either returns an existing stream, a newly opened stream, or nil if a stream with the provided ID is already closed.
|
|
// Newly opened streams should only originate from the client. To open a stream from the server, OpenStream should be used.
|
|
func (m *streamsMapLegacy) GetOrOpenStream(id protocol.StreamID) (streamI, error) {
|
|
m.mutex.RLock()
|
|
s, ok := m.streams[id]
|
|
m.mutex.RUnlock()
|
|
if ok {
|
|
return s, nil
|
|
}
|
|
|
|
// ... we don't have an existing stream
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
// We need to check whether another invocation has already created a stream (between RUnlock() and Lock()).
|
|
s, ok = m.streams[id]
|
|
if ok {
|
|
return s, nil
|
|
}
|
|
|
|
if m.perspective == m.streamInitiatedBy(id) {
|
|
if id <= m.nextStreamToOpen { // this is a stream opened by us. Must have been closed already
|
|
return nil, nil
|
|
}
|
|
return nil, qerr.Error(qerr.InvalidStreamID, fmt.Sprintf("peer attempted to open stream %d", id))
|
|
}
|
|
if id <= m.highestStreamOpenedByPeer { // this is a peer-initiated stream that doesn't exist anymore. Must have been closed already
|
|
return nil, nil
|
|
}
|
|
|
|
for sid := m.highestStreamOpenedByPeer + 2; sid <= id; sid += 2 {
|
|
if _, err := m.openRemoteStream(sid); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
m.nextStreamOrErrCond.Broadcast()
|
|
return m.streams[id], nil
|
|
}
|
|
|
|
func (m *streamsMapLegacy) openRemoteStream(id protocol.StreamID) (streamI, error) {
|
|
if m.numIncomingStreams >= m.maxIncomingStreams {
|
|
return nil, qerr.TooManyOpenStreams
|
|
}
|
|
if id+protocol.MaxNewStreamIDDelta < m.highestStreamOpenedByPeer {
|
|
return nil, qerr.Error(qerr.InvalidStreamID, fmt.Sprintf("attempted to open stream %d, which is a lot smaller than the highest opened stream, %d", id, m.highestStreamOpenedByPeer))
|
|
}
|
|
|
|
m.numIncomingStreams++
|
|
if id > m.highestStreamOpenedByPeer {
|
|
m.highestStreamOpenedByPeer = id
|
|
}
|
|
|
|
s := m.newStream(id)
|
|
m.putStream(s)
|
|
return s, nil
|
|
}
|
|
|
|
func (m *streamsMapLegacy) openStreamImpl() (streamI, error) {
|
|
if m.numOutgoingStreams >= m.maxOutgoingStreams {
|
|
return nil, qerr.TooManyOpenStreams
|
|
}
|
|
|
|
m.numOutgoingStreams++
|
|
s := m.newStream(m.nextStreamToOpen)
|
|
m.putStream(s)
|
|
m.nextStreamToOpen += 2
|
|
return s, nil
|
|
}
|
|
|
|
// OpenStream opens the next available stream
|
|
func (m *streamsMapLegacy) OpenStream() (Stream, error) {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
|
|
if m.closeErr != nil {
|
|
return nil, m.closeErr
|
|
}
|
|
return m.openStreamImpl()
|
|
}
|
|
|
|
func (m *streamsMapLegacy) OpenStreamSync() (Stream, error) {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
|
|
for {
|
|
if m.closeErr != nil {
|
|
return nil, m.closeErr
|
|
}
|
|
str, err := m.openStreamImpl()
|
|
if err == nil {
|
|
return str, err
|
|
}
|
|
if err != nil && err != qerr.TooManyOpenStreams {
|
|
return nil, err
|
|
}
|
|
m.openStreamOrErrCond.Wait()
|
|
}
|
|
}
|
|
|
|
// AcceptStream returns the next stream opened by the peer
|
|
// it blocks until a new stream is opened
|
|
func (m *streamsMapLegacy) AcceptStream() (Stream, error) {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
var str streamI
|
|
for {
|
|
var ok bool
|
|
if m.closeErr != nil {
|
|
return nil, m.closeErr
|
|
}
|
|
str, ok = m.streams[m.nextStreamToAccept]
|
|
if ok {
|
|
break
|
|
}
|
|
m.nextStreamOrErrCond.Wait()
|
|
}
|
|
m.nextStreamToAccept += 2
|
|
return str, nil
|
|
}
|
|
|
|
func (m *streamsMapLegacy) DeleteStream(id protocol.StreamID) error {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
_, ok := m.streams[id]
|
|
if !ok {
|
|
return errMapAccess
|
|
}
|
|
delete(m.streams, id)
|
|
if m.streamInitiatedBy(id) == m.perspective {
|
|
m.numOutgoingStreams--
|
|
} else {
|
|
m.numIncomingStreams--
|
|
}
|
|
m.openStreamOrErrCond.Signal()
|
|
return nil
|
|
}
|
|
|
|
func (m *streamsMapLegacy) putStream(s streamI) error {
|
|
id := s.StreamID()
|
|
if _, ok := m.streams[id]; ok {
|
|
return fmt.Errorf("a stream with ID %d already exists", id)
|
|
}
|
|
m.streams[id] = s
|
|
return nil
|
|
}
|
|
|
|
func (m *streamsMapLegacy) CloseWithError(err error) {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
m.closeErr = err
|
|
m.nextStreamOrErrCond.Broadcast()
|
|
m.openStreamOrErrCond.Broadcast()
|
|
for _, s := range m.streams {
|
|
s.closeForShutdown(err)
|
|
}
|
|
}
|
|
|
|
// TODO(#952): this won't be needed when gQUIC supports stateless handshakes
|
|
func (m *streamsMapLegacy) UpdateLimits(params *handshake.TransportParameters) {
|
|
m.mutex.Lock()
|
|
m.maxOutgoingStreams = params.MaxStreams
|
|
for id, str := range m.streams {
|
|
str.handleMaxStreamDataFrame(&wire.MaxStreamDataFrame{
|
|
StreamID: id,
|
|
ByteOffset: params.StreamFlowControlWindow,
|
|
})
|
|
}
|
|
m.mutex.Unlock()
|
|
m.openStreamOrErrCond.Broadcast()
|
|
}
|