github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/http3/frames.go (about)

     1  package http3
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/metacubex/quic-go"
    10  	"github.com/metacubex/quic-go/quicvarint"
    11  )
    12  
    13  // FrameType is the frame type of a HTTP/3 frame
    14  type FrameType uint64
    15  
    16  type unknownFrameHandlerFunc func(FrameType, error) (processed bool, err error)
    17  
    18  type frame interface{}
    19  
    20  var errHijacked = errors.New("hijacked")
    21  
    22  type frameParser struct {
    23  	r                   io.Reader
    24  	conn                quic.Connection
    25  	unknownFrameHandler unknownFrameHandlerFunc
    26  }
    27  
    28  func (p *frameParser) ParseNext() (frame, error) {
    29  	qr := quicvarint.NewReader(p.r)
    30  	for {
    31  		t, err := quicvarint.Read(qr)
    32  		if err != nil {
    33  			if p.unknownFrameHandler != nil {
    34  				hijacked, err := p.unknownFrameHandler(0, err)
    35  				if err != nil {
    36  					return nil, err
    37  				}
    38  				if hijacked {
    39  					return nil, errHijacked
    40  				}
    41  			}
    42  			return nil, err
    43  		}
    44  		// Call the unknownFrameHandler for frames not defined in the HTTP/3 spec
    45  		if t > 0xd && p.unknownFrameHandler != nil {
    46  			hijacked, err := p.unknownFrameHandler(FrameType(t), nil)
    47  			if err != nil {
    48  				return nil, err
    49  			}
    50  			if hijacked {
    51  				return nil, errHijacked
    52  			}
    53  			// If the unknownFrameHandler didn't process the frame, it is our responsibility to skip it.
    54  		}
    55  		l, err := quicvarint.Read(qr)
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  
    60  		switch t {
    61  		case 0x0:
    62  			return &dataFrame{Length: l}, nil
    63  		case 0x1:
    64  			return &headersFrame{Length: l}, nil
    65  		case 0x4:
    66  			return parseSettingsFrame(p.r, l)
    67  		case 0x3: // CANCEL_PUSH
    68  		case 0x5: // PUSH_PROMISE
    69  		case 0x7: // GOAWAY
    70  		case 0xd: // MAX_PUSH_ID
    71  		case 0x2, 0x6, 0x8, 0x9:
    72  			p.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "")
    73  			return nil, fmt.Errorf("http3: reserved frame type: %d", t)
    74  		}
    75  		// skip over unknown frames
    76  		if _, err := io.CopyN(io.Discard, qr, int64(l)); err != nil {
    77  			return nil, err
    78  		}
    79  	}
    80  }
    81  
    82  type dataFrame struct {
    83  	Length uint64
    84  }
    85  
    86  func (f *dataFrame) Append(b []byte) []byte {
    87  	b = quicvarint.Append(b, 0x0)
    88  	return quicvarint.Append(b, f.Length)
    89  }
    90  
    91  type headersFrame struct {
    92  	Length uint64
    93  }
    94  
    95  func (f *headersFrame) Append(b []byte) []byte {
    96  	b = quicvarint.Append(b, 0x1)
    97  	return quicvarint.Append(b, f.Length)
    98  }
    99  
   100  const (
   101  	// Extended CONNECT, RFC 9220
   102  	settingExtendedConnect = 0x8
   103  	// HTTP Datagrams, RFC 9297
   104  	settingDatagram = 0x33
   105  )
   106  
   107  type settingsFrame struct {
   108  	Datagram        bool // HTTP Datagrams, RFC 9297
   109  	ExtendedConnect bool // Extended CONNECT, RFC 9220
   110  
   111  	Other map[uint64]uint64 // all settings that we don't explicitly recognize
   112  }
   113  
   114  func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
   115  	if l > 8*(1<<10) {
   116  		return nil, fmt.Errorf("unexpected size for SETTINGS frame: %d", l)
   117  	}
   118  	buf := make([]byte, l)
   119  	if _, err := io.ReadFull(r, buf); err != nil {
   120  		if err == io.ErrUnexpectedEOF {
   121  			return nil, io.EOF
   122  		}
   123  		return nil, err
   124  	}
   125  	frame := &settingsFrame{}
   126  	b := bytes.NewReader(buf)
   127  	var readDatagram, readExtendedConnect bool
   128  	for b.Len() > 0 {
   129  		id, err := quicvarint.Read(b)
   130  		if err != nil { // should not happen. We allocated the whole frame already.
   131  			return nil, err
   132  		}
   133  		val, err := quicvarint.Read(b)
   134  		if err != nil { // should not happen. We allocated the whole frame already.
   135  			return nil, err
   136  		}
   137  
   138  		switch id {
   139  		case settingExtendedConnect:
   140  			if readExtendedConnect {
   141  				return nil, fmt.Errorf("duplicate setting: %d", id)
   142  			}
   143  			readExtendedConnect = true
   144  			if val != 0 && val != 1 {
   145  				return nil, fmt.Errorf("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: %d", val)
   146  			}
   147  			frame.ExtendedConnect = val == 1
   148  		case settingDatagram:
   149  			if readDatagram {
   150  				return nil, fmt.Errorf("duplicate setting: %d", id)
   151  			}
   152  			readDatagram = true
   153  			if val != 0 && val != 1 {
   154  				return nil, fmt.Errorf("invalid value for SETTINGS_H3_DATAGRAM: %d", val)
   155  			}
   156  			frame.Datagram = val == 1
   157  		default:
   158  			if _, ok := frame.Other[id]; ok {
   159  				return nil, fmt.Errorf("duplicate setting: %d", id)
   160  			}
   161  			if frame.Other == nil {
   162  				frame.Other = make(map[uint64]uint64)
   163  			}
   164  			frame.Other[id] = val
   165  		}
   166  	}
   167  	return frame, nil
   168  }
   169  
   170  func (f *settingsFrame) Append(b []byte) []byte {
   171  	b = quicvarint.Append(b, 0x4)
   172  	var l int
   173  	for id, val := range f.Other {
   174  		l += quicvarint.Len(id) + quicvarint.Len(val)
   175  	}
   176  	if f.Datagram {
   177  		l += quicvarint.Len(settingDatagram) + quicvarint.Len(1)
   178  	}
   179  	if f.ExtendedConnect {
   180  		l += quicvarint.Len(settingExtendedConnect) + quicvarint.Len(1)
   181  	}
   182  	b = quicvarint.Append(b, uint64(l))
   183  	if f.Datagram {
   184  		b = quicvarint.Append(b, settingDatagram)
   185  		b = quicvarint.Append(b, 1)
   186  	}
   187  	if f.ExtendedConnect {
   188  		b = quicvarint.Append(b, settingExtendedConnect)
   189  		b = quicvarint.Append(b, 1)
   190  	}
   191  	for id, val := range f.Other {
   192  		b = quicvarint.Append(b, id)
   193  		b = quicvarint.Append(b, val)
   194  	}
   195  	return b
   196  }