Source file src/net/http/internal/http3/conn.go

     1  // Copyright 2025 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  package http3
     6  
     7  import (
     8  	"context"
     9  	"io"
    10  	"sync"
    11  
    12  	"golang.org/x/net/quic"
    13  )
    14  
    15  type streamHandler interface {
    16  	handleControlStream(*stream) error
    17  	handlePushStream(*stream) error
    18  	handleEncoderStream(*stream) error
    19  	handleDecoderStream(*stream) error
    20  	handleRequestStream(*stream) error
    21  	abort(error)
    22  }
    23  
    24  type genericConn struct {
    25  	mu sync.Mutex
    26  
    27  	// The peer may create exactly one control, encoder, and decoder stream.
    28  	// streamsCreated is a bitset of streams created so far.
    29  	// Bits are 1 << streamType.
    30  	streamsCreated uint8
    31  }
    32  
    33  func (c *genericConn) acceptStreams(qconn *quic.Conn, h streamHandler) {
    34  	for {
    35  		// Use context.Background: This blocks until a stream is accepted
    36  		// or the connection closes.
    37  		st, err := qconn.AcceptStream(context.Background())
    38  		if err != nil {
    39  			return // connection closed
    40  		}
    41  		if st.IsReadOnly() {
    42  			go c.handleUnidirectionalStream(newStream(st), h)
    43  		} else {
    44  			go c.handleRequestStream(newStream(st), h)
    45  		}
    46  	}
    47  }
    48  
    49  func (c *genericConn) handleUnidirectionalStream(st *stream, h streamHandler) {
    50  	// Unidirectional stream header: One varint with the stream type.
    51  	v, err := st.readVarint()
    52  	if err != nil {
    53  		h.abort(&connectionError{
    54  			code:    errH3StreamCreationError,
    55  			message: "error reading unidirectional stream header",
    56  		})
    57  		return
    58  	}
    59  	stype := streamType(v)
    60  	if err := c.checkStreamCreation(stype); err != nil {
    61  		h.abort(err)
    62  		return
    63  	}
    64  	switch stype {
    65  	case streamTypeControl:
    66  		err = h.handleControlStream(st)
    67  	case streamTypePush:
    68  		err = h.handlePushStream(st)
    69  	case streamTypeEncoder:
    70  		err = h.handleEncoderStream(st)
    71  	case streamTypeDecoder:
    72  		err = h.handleDecoderStream(st)
    73  	default:
    74  		// "Recipients of unknown stream types MUST either abort reading
    75  		// of the stream or discard incoming data without further processing."
    76  		// https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2-7
    77  		err = &streamError{
    78  			code:    errH3StreamCreationError,
    79  			message: "unknown stream type",
    80  		}
    81  	}
    82  	if err == io.EOF {
    83  		err = &connectionError{
    84  			code:    errH3ClosedCriticalStream,
    85  			message: streamType(stype).String() + " stream closed",
    86  		}
    87  	}
    88  	c.handleStreamError(st, h, err)
    89  }
    90  
    91  func (c *genericConn) handleRequestStream(st *stream, h streamHandler) {
    92  	c.handleStreamError(st, h, h.handleRequestStream(st))
    93  }
    94  
    95  func (c *genericConn) handleStreamError(st *stream, h streamHandler, err error) {
    96  	switch err := err.(type) {
    97  	case *connectionError:
    98  		h.abort(err)
    99  	case nil:
   100  		st.CloseRead(uint64(errH3NoError))
   101  		st.CloseWrite()
   102  	case *streamError:
   103  		st.CloseRead(uint64(err.code))
   104  		st.Reset(uint64(err.code))
   105  	default:
   106  		st.CloseRead(uint64(errH3InternalError))
   107  		st.Reset(uint64(errH3InternalError))
   108  	}
   109  }
   110  
   111  func (c *genericConn) checkStreamCreation(stype streamType) error {
   112  	switch stype {
   113  	case streamTypeControl, streamTypeEncoder, streamTypeDecoder:
   114  		// The peer may create exactly one control, encoder, and decoder stream.
   115  	default:
   116  		return nil
   117  	}
   118  	c.mu.Lock()
   119  	defer c.mu.Unlock()
   120  	bit := uint8(1) << stype
   121  	if c.streamsCreated&bit != 0 {
   122  		return &connectionError{
   123  			code:    errH3StreamCreationError,
   124  			message: "multiple " + stype.String() + " streams created",
   125  		}
   126  	}
   127  	c.streamsCreated |= bit
   128  	return nil
   129  }
   130  

View as plain text