github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/http3/frames.go (about)

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