github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/wire/connection_close_frame.go (about)

     1  package wire
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  
     7  	"github.com/apernet/quic-go/internal/protocol"
     8  	"github.com/apernet/quic-go/quicvarint"
     9  )
    10  
    11  // A ConnectionCloseFrame is a CONNECTION_CLOSE frame
    12  type ConnectionCloseFrame struct {
    13  	IsApplicationError bool
    14  	ErrorCode          uint64
    15  	FrameType          uint64
    16  	ReasonPhrase       string
    17  }
    18  
    19  func parseConnectionCloseFrame(r *bytes.Reader, typ uint64, _ protocol.Version) (*ConnectionCloseFrame, error) {
    20  	f := &ConnectionCloseFrame{IsApplicationError: typ == applicationCloseFrameType}
    21  	ec, err := quicvarint.Read(r)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  	f.ErrorCode = ec
    26  	// read the Frame Type, if this is not an application error
    27  	if !f.IsApplicationError {
    28  		ft, err := quicvarint.Read(r)
    29  		if err != nil {
    30  			return nil, err
    31  		}
    32  		f.FrameType = ft
    33  	}
    34  	var reasonPhraseLen uint64
    35  	reasonPhraseLen, err = quicvarint.Read(r)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	// shortcut to prevent the unnecessary allocation of dataLen bytes
    40  	// if the dataLen is larger than the remaining length of the packet
    41  	// reading the whole reason phrase would result in EOF when attempting to READ
    42  	if int(reasonPhraseLen) > r.Len() {
    43  		return nil, io.EOF
    44  	}
    45  
    46  	reasonPhrase := make([]byte, reasonPhraseLen)
    47  	if _, err := io.ReadFull(r, reasonPhrase); err != nil {
    48  		// this should never happen, since we already checked the reasonPhraseLen earlier
    49  		return nil, err
    50  	}
    51  	f.ReasonPhrase = string(reasonPhrase)
    52  	return f, nil
    53  }
    54  
    55  // Length of a written frame
    56  func (f *ConnectionCloseFrame) Length(protocol.Version) protocol.ByteCount {
    57  	length := 1 + protocol.ByteCount(quicvarint.Len(f.ErrorCode)+quicvarint.Len(uint64(len(f.ReasonPhrase)))) + protocol.ByteCount(len(f.ReasonPhrase))
    58  	if !f.IsApplicationError {
    59  		length += protocol.ByteCount(quicvarint.Len(f.FrameType)) // for the frame type
    60  	}
    61  	return length
    62  }
    63  
    64  func (f *ConnectionCloseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) {
    65  	if f.IsApplicationError {
    66  		b = append(b, applicationCloseFrameType)
    67  	} else {
    68  		b = append(b, connectionCloseFrameType)
    69  	}
    70  
    71  	b = quicvarint.Append(b, f.ErrorCode)
    72  	if !f.IsApplicationError {
    73  		b = quicvarint.Append(b, f.FrameType)
    74  	}
    75  	b = quicvarint.Append(b, uint64(len(f.ReasonPhrase)))
    76  	b = append(b, []byte(f.ReasonPhrase)...)
    77  	return b, nil
    78  }