golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/conn_streams.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"context"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  )
    15  
    16  type streamsState struct {
    17  	queue queue[*Stream] // new, peer-created streams
    18  
    19  	// All peer-created streams.
    20  	//
    21  	// Implicitly created streams are included as an empty entry in the map.
    22  	// (For example, if we receive a frame for stream 4, we implicitly create stream 0 and
    23  	// insert an empty entry for it to the map.)
    24  	//
    25  	// The map value is maybeStream rather than *Stream as a reminder that values can be nil.
    26  	streams map[streamID]maybeStream
    27  
    28  	// Limits on the number of streams, indexed by streamType.
    29  	localLimit  [streamTypeCount]localStreamLimits
    30  	remoteLimit [streamTypeCount]remoteStreamLimits
    31  
    32  	// Peer configuration provided in transport parameters.
    33  	peerInitialMaxStreamDataRemote    [streamTypeCount]int64 // streams opened by us
    34  	peerInitialMaxStreamDataBidiLocal int64                  // streams opened by them
    35  
    36  	// Connection-level flow control.
    37  	inflow  connInflow
    38  	outflow connOutflow
    39  
    40  	// Streams with frames to send are stored in one of two circular linked lists,
    41  	// depending on whether they require connection-level flow control.
    42  	needSend  atomic.Bool
    43  	sendMu    sync.Mutex
    44  	queueMeta streamRing // streams with any non-flow-controlled frames
    45  	queueData streamRing // streams with only flow-controlled frames
    46  }
    47  
    48  // maybeStream is a possibly nil *Stream. See streamsState.streams.
    49  type maybeStream struct {
    50  	s *Stream
    51  }
    52  
    53  func (c *Conn) streamsInit() {
    54  	c.streams.streams = make(map[streamID]maybeStream)
    55  	c.streams.queue = newQueue[*Stream]()
    56  	c.streams.localLimit[bidiStream].init()
    57  	c.streams.localLimit[uniStream].init()
    58  	c.streams.remoteLimit[bidiStream].init(c.config.maxBidiRemoteStreams())
    59  	c.streams.remoteLimit[uniStream].init(c.config.maxUniRemoteStreams())
    60  	c.inflowInit()
    61  }
    62  
    63  func (c *Conn) streamsCleanup() {
    64  	c.streams.queue.close(errConnClosed)
    65  	c.streams.localLimit[bidiStream].connHasClosed()
    66  	c.streams.localLimit[uniStream].connHasClosed()
    67  	for _, s := range c.streams.streams {
    68  		if s.s != nil {
    69  			s.s.connHasClosed()
    70  		}
    71  	}
    72  }
    73  
    74  // AcceptStream waits for and returns the next stream created by the peer.
    75  func (c *Conn) AcceptStream(ctx context.Context) (*Stream, error) {
    76  	return c.streams.queue.get(ctx, c.testHooks)
    77  }
    78  
    79  // NewStream creates a stream.
    80  //
    81  // If the peer's maximum stream limit for the connection has been reached,
    82  // NewStream blocks until the limit is increased or the context expires.
    83  func (c *Conn) NewStream(ctx context.Context) (*Stream, error) {
    84  	return c.newLocalStream(ctx, bidiStream)
    85  }
    86  
    87  // NewSendOnlyStream creates a unidirectional, send-only stream.
    88  //
    89  // If the peer's maximum stream limit for the connection has been reached,
    90  // NewSendOnlyStream blocks until the limit is increased or the context expires.
    91  func (c *Conn) NewSendOnlyStream(ctx context.Context) (*Stream, error) {
    92  	return c.newLocalStream(ctx, uniStream)
    93  }
    94  
    95  func (c *Conn) newLocalStream(ctx context.Context, styp streamType) (*Stream, error) {
    96  	num, err := c.streams.localLimit[styp].open(ctx, c)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	s := newStream(c, newStreamID(c.side, styp, num))
   102  	s.outmaxbuf = c.config.maxStreamWriteBufferSize()
   103  	s.outwin = c.streams.peerInitialMaxStreamDataRemote[styp]
   104  	if styp == bidiStream {
   105  		s.inmaxbuf = c.config.maxStreamReadBufferSize()
   106  		s.inwin = c.config.maxStreamReadBufferSize()
   107  	}
   108  	s.inUnlock()
   109  	s.outUnlock()
   110  
   111  	// Modify c.streams on the conn's loop.
   112  	if err := c.runOnLoop(ctx, func(now time.Time, c *Conn) {
   113  		c.streams.streams[s.id] = maybeStream{s}
   114  	}); err != nil {
   115  		return nil, err
   116  	}
   117  	return s, nil
   118  }
   119  
   120  // streamFrameType identifies which direction of a stream,
   121  // from the local perspective, a frame is associated with.
   122  //
   123  // For example, STREAM is a recvStream frame,
   124  // because it carries data from the peer to us.
   125  type streamFrameType uint8
   126  
   127  const (
   128  	sendStream = streamFrameType(iota) // for example, MAX_DATA
   129  	recvStream                         // for example, STREAM_DATA_BLOCKED
   130  )
   131  
   132  // streamForID returns the stream with the given id.
   133  // If the stream does not exist, it returns nil.
   134  func (c *Conn) streamForID(id streamID) *Stream {
   135  	return c.streams.streams[id].s
   136  }
   137  
   138  // streamForFrame returns the stream with the given id.
   139  // If the stream does not exist, it may be created.
   140  //
   141  // streamForFrame aborts the connection if the stream id, state, and frame type don't align.
   142  // For example, it aborts the connection with a STREAM_STATE error if a MAX_DATA frame
   143  // is received for a receive-only stream, or if the peer attempts to create a stream that
   144  // should be originated locally.
   145  //
   146  // streamForFrame returns nil if the stream no longer exists or if an error occurred.
   147  func (c *Conn) streamForFrame(now time.Time, id streamID, ftype streamFrameType) *Stream {
   148  	if id.streamType() == uniStream {
   149  		if (id.initiator() == c.side) != (ftype == sendStream) {
   150  			// Received an invalid frame for unidirectional stream.
   151  			// For example, a RESET_STREAM frame for a send-only stream.
   152  			c.abort(now, localTransportError{
   153  				code:   errStreamState,
   154  				reason: "invalid frame for unidirectional stream",
   155  			})
   156  			return nil
   157  		}
   158  	}
   159  
   160  	ms, isOpen := c.streams.streams[id]
   161  	if ms.s != nil {
   162  		return ms.s
   163  	}
   164  
   165  	num := id.num()
   166  	styp := id.streamType()
   167  	if id.initiator() == c.side {
   168  		if num < c.streams.localLimit[styp].opened {
   169  			// This stream was created by us, and has been closed.
   170  			return nil
   171  		}
   172  		// Received a frame for a stream that should be originated by us,
   173  		// but which we never created.
   174  		c.abort(now, localTransportError{
   175  			code:   errStreamState,
   176  			reason: "received frame for unknown stream",
   177  		})
   178  		return nil
   179  	} else {
   180  		// if isOpen, this is a stream that was implicitly opened by a
   181  		// previous frame for a larger-numbered stream, but we haven't
   182  		// actually created it yet.
   183  		if !isOpen && num < c.streams.remoteLimit[styp].opened {
   184  			// This stream was created by the peer, and has been closed.
   185  			return nil
   186  		}
   187  	}
   188  
   189  	prevOpened := c.streams.remoteLimit[styp].opened
   190  	if err := c.streams.remoteLimit[styp].open(id); err != nil {
   191  		c.abort(now, err)
   192  		return nil
   193  	}
   194  
   195  	// Receiving a frame for a stream implicitly creates all streams
   196  	// with the same initiator and type and a lower number.
   197  	// Add a nil entry to the streams map for each implicitly created stream.
   198  	for n := newStreamID(id.initiator(), id.streamType(), prevOpened); n < id; n += 4 {
   199  		c.streams.streams[n] = maybeStream{}
   200  	}
   201  
   202  	s := newStream(c, id)
   203  	s.inmaxbuf = c.config.maxStreamReadBufferSize()
   204  	s.inwin = c.config.maxStreamReadBufferSize()
   205  	if id.streamType() == bidiStream {
   206  		s.outmaxbuf = c.config.maxStreamWriteBufferSize()
   207  		s.outwin = c.streams.peerInitialMaxStreamDataBidiLocal
   208  	}
   209  	s.inUnlock()
   210  	s.outUnlock()
   211  
   212  	c.streams.streams[id] = maybeStream{s}
   213  	c.streams.queue.put(s)
   214  	return s
   215  }
   216  
   217  // maybeQueueStreamForSend marks a stream as containing frames that need sending.
   218  func (c *Conn) maybeQueueStreamForSend(s *Stream, state streamState) {
   219  	if state.wantQueue() == state.inQueue() {
   220  		return // already on the right queue
   221  	}
   222  	c.streams.sendMu.Lock()
   223  	defer c.streams.sendMu.Unlock()
   224  	state = s.state.load() // may have changed while waiting
   225  	c.queueStreamForSendLocked(s, state)
   226  
   227  	c.streams.needSend.Store(true)
   228  	c.wake()
   229  }
   230  
   231  // queueStreamForSendLocked moves a stream to the correct send queue,
   232  // or removes it from all queues.
   233  //
   234  // state is the last known stream state.
   235  func (c *Conn) queueStreamForSendLocked(s *Stream, state streamState) {
   236  	for {
   237  		wantQueue := state.wantQueue()
   238  		inQueue := state.inQueue()
   239  		if inQueue == wantQueue {
   240  			return // already on the right queue
   241  		}
   242  
   243  		switch inQueue {
   244  		case metaQueue:
   245  			c.streams.queueMeta.remove(s)
   246  		case dataQueue:
   247  			c.streams.queueData.remove(s)
   248  		}
   249  
   250  		switch wantQueue {
   251  		case metaQueue:
   252  			c.streams.queueMeta.append(s)
   253  			state = s.state.set(streamQueueMeta, streamQueueMeta|streamQueueData)
   254  		case dataQueue:
   255  			c.streams.queueData.append(s)
   256  			state = s.state.set(streamQueueData, streamQueueMeta|streamQueueData)
   257  		case noQueue:
   258  			state = s.state.set(0, streamQueueMeta|streamQueueData)
   259  		}
   260  
   261  		// If the stream state changed while we were moving the stream,
   262  		// we might now be on the wrong queue.
   263  		//
   264  		// For example:
   265  		//   - stream has data to send: streamOutSendData|streamQueueData
   266  		//   - appendStreamFrames sends all the data: streamQueueData
   267  		//   - concurrently, more data is written: streamOutSendData|streamQueueData
   268  		//   - appendStreamFrames calls us with the last state it observed
   269  		//     (streamQueueData).
   270  		//   - We remove the stream from the queue and observe the updated state:
   271  		//     streamOutSendData
   272  		//   - We realize that the stream needs to go back on the data queue.
   273  		//
   274  		// Go back around the loop to confirm we're on the correct queue.
   275  	}
   276  }
   277  
   278  // appendStreamFrames writes stream-related frames to the current packet.
   279  //
   280  // It returns true if no more frames need appending,
   281  // false if not everything fit in the current packet.
   282  func (c *Conn) appendStreamFrames(w *packetWriter, pnum packetNumber, pto bool) bool {
   283  	// MAX_DATA
   284  	if !c.appendMaxDataFrame(w, pnum, pto) {
   285  		return false
   286  	}
   287  
   288  	// MAX_STREAM_DATA
   289  	if !c.streams.remoteLimit[uniStream].appendFrame(w, uniStream, pnum, pto) {
   290  		return false
   291  	}
   292  	if !c.streams.remoteLimit[bidiStream].appendFrame(w, bidiStream, pnum, pto) {
   293  		return false
   294  	}
   295  
   296  	if pto {
   297  		return c.appendStreamFramesPTO(w, pnum)
   298  	}
   299  	if !c.streams.needSend.Load() {
   300  		return true
   301  	}
   302  	c.streams.sendMu.Lock()
   303  	defer c.streams.sendMu.Unlock()
   304  	// queueMeta contains streams with non-flow-controlled frames to send.
   305  	for c.streams.queueMeta.head != nil {
   306  		s := c.streams.queueMeta.head
   307  		state := s.state.load()
   308  		if state&(streamQueueMeta|streamConnRemoved) != streamQueueMeta {
   309  			panic("BUG: queueMeta stream is not streamQueueMeta")
   310  		}
   311  		if state&streamInSendMeta != 0 {
   312  			s.ingate.lock()
   313  			ok := s.appendInFramesLocked(w, pnum, pto)
   314  			state = s.inUnlockNoQueue()
   315  			if !ok {
   316  				return false
   317  			}
   318  			if state&streamInSendMeta != 0 {
   319  				panic("BUG: streamInSendMeta set after successfully appending frames")
   320  			}
   321  		}
   322  		if state&streamOutSendMeta != 0 {
   323  			s.outgate.lock()
   324  			// This might also append flow-controlled frames if we have any
   325  			// and available conn-level quota. That's fine.
   326  			ok := s.appendOutFramesLocked(w, pnum, pto)
   327  			state = s.outUnlockNoQueue()
   328  			// We're checking both ok and state, because appendOutFramesLocked
   329  			// might have filled up the packet with flow-controlled data.
   330  			// If so, we want to move the stream to queueData for any remaining frames.
   331  			if !ok && state&streamOutSendMeta != 0 {
   332  				return false
   333  			}
   334  			if state&streamOutSendMeta != 0 {
   335  				panic("BUG: streamOutSendMeta set after successfully appending frames")
   336  			}
   337  		}
   338  		// We've sent all frames for this stream, so remove it from the send queue.
   339  		c.streams.queueMeta.remove(s)
   340  		if state&(streamInDone|streamOutDone) == streamInDone|streamOutDone {
   341  			// Stream is finished, remove it from the conn.
   342  			state = s.state.set(streamConnRemoved, streamQueueMeta|streamConnRemoved)
   343  			delete(c.streams.streams, s.id)
   344  
   345  			// Record finalization of remote streams, to know when
   346  			// to extend the peer's stream limit.
   347  			if s.id.initiator() != c.side {
   348  				c.streams.remoteLimit[s.id.streamType()].close()
   349  			}
   350  		} else {
   351  			state = s.state.set(0, streamQueueMeta|streamConnRemoved)
   352  		}
   353  		// The stream may have flow-controlled data to send,
   354  		// or something might have added non-flow-controlled frames after we
   355  		// unlocked the stream.
   356  		// If so, put the stream back on a queue.
   357  		c.queueStreamForSendLocked(s, state)
   358  	}
   359  	// queueData contains streams with flow-controlled frames.
   360  	for c.streams.queueData.head != nil {
   361  		avail := c.streams.outflow.avail()
   362  		if avail == 0 {
   363  			break // no flow control quota available
   364  		}
   365  		s := c.streams.queueData.head
   366  		s.outgate.lock()
   367  		ok := s.appendOutFramesLocked(w, pnum, pto)
   368  		state := s.outUnlockNoQueue()
   369  		if !ok {
   370  			// We've sent some data for this stream, but it still has more to send.
   371  			// If the stream got a reasonable chance to put data in a packet,
   372  			// advance sendHead to the next stream in line, to avoid starvation.
   373  			// We'll come back to this stream after going through the others.
   374  			//
   375  			// If the packet was already mostly out of space, leave sendHead alone
   376  			// and come back to this stream again on the next packet.
   377  			if avail > 512 {
   378  				c.streams.queueData.head = s.next
   379  			}
   380  			return false
   381  		}
   382  		if state&streamQueueData == 0 {
   383  			panic("BUG: queueData stream is not streamQueueData")
   384  		}
   385  		if state&streamOutSendData != 0 {
   386  			// We must have run out of connection-level flow control:
   387  			// appendOutFramesLocked says it wrote all it can, but there's
   388  			// still data to send.
   389  			//
   390  			// Advance sendHead to the next stream in line to avoid starvation.
   391  			if c.streams.outflow.avail() != 0 {
   392  				panic("BUG: streamOutSendData set and flow control available after send")
   393  			}
   394  			c.streams.queueData.head = s.next
   395  			return true
   396  		}
   397  		c.streams.queueData.remove(s)
   398  		state = s.state.set(0, streamQueueData)
   399  		c.queueStreamForSendLocked(s, state)
   400  	}
   401  	if c.streams.queueMeta.head == nil && c.streams.queueData.head == nil {
   402  		c.streams.needSend.Store(false)
   403  	}
   404  	return true
   405  }
   406  
   407  // appendStreamFramesPTO writes stream-related frames to the current packet
   408  // for a PTO probe.
   409  //
   410  // It returns true if no more frames need appending,
   411  // false if not everything fit in the current packet.
   412  func (c *Conn) appendStreamFramesPTO(w *packetWriter, pnum packetNumber) bool {
   413  	c.streams.sendMu.Lock()
   414  	defer c.streams.sendMu.Unlock()
   415  	const pto = true
   416  	for _, ms := range c.streams.streams {
   417  		s := ms.s
   418  		if s == nil {
   419  			continue
   420  		}
   421  		const pto = true
   422  		s.ingate.lock()
   423  		inOK := s.appendInFramesLocked(w, pnum, pto)
   424  		s.inUnlockNoQueue()
   425  		if !inOK {
   426  			return false
   427  		}
   428  
   429  		s.outgate.lock()
   430  		outOK := s.appendOutFramesLocked(w, pnum, pto)
   431  		s.outUnlockNoQueue()
   432  		if !outOK {
   433  			return false
   434  		}
   435  	}
   436  	return true
   437  }
   438  
   439  // A streamRing is a circular linked list of streams.
   440  type streamRing struct {
   441  	head *Stream
   442  }
   443  
   444  // remove removes s from the ring.
   445  // s must be on the ring.
   446  func (r *streamRing) remove(s *Stream) {
   447  	if s.next == s {
   448  		r.head = nil // s was the last stream in the ring
   449  	} else {
   450  		s.prev.next = s.next
   451  		s.next.prev = s.prev
   452  		if r.head == s {
   453  			r.head = s.next
   454  		}
   455  	}
   456  }
   457  
   458  // append places s at the last position in the ring.
   459  // s must not be attached to any ring.
   460  func (r *streamRing) append(s *Stream) {
   461  	if r.head == nil {
   462  		r.head = s
   463  		s.next = s
   464  		s.prev = s
   465  	} else {
   466  		s.prev = r.head.prev
   467  		s.next = r.head
   468  		s.prev.next = s
   469  		s.next.prev = s
   470  	}
   471  }