package quic import ( "bytes" "sync" "sync/atomic" "github.com/lucas-clemente/quic-go/crypto" "github.com/lucas-clemente/quic-go/frames" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/utils" ) type packedPacket struct { number protocol.PacketNumber entropyBit bool raw []byte payload []byte } type packetPacker struct { connectionID protocol.ConnectionID aead crypto.AEAD queuedFrames []frames.Frame mutex sync.Mutex lastPacketNumber protocol.PacketNumber } func (p *packetPacker) AddFrame(f frames.Frame) { p.mutex.Lock() p.queuedFrames = append(p.queuedFrames, f) p.mutex.Unlock() } func (p *packetPacker) PackPacket() (*packedPacket, error) { p.mutex.Lock() defer p.mutex.Unlock() // TODO: Split up? if len(p.queuedFrames) == 0 { return nil, nil } currentPacketNumber := protocol.PacketNumber(atomic.AddUint64( (*uint64)(&p.lastPacketNumber), 1, )) payload, err := p.composeNextPayload(currentPacketNumber) if err != nil { return nil, err } entropyBit, err := utils.RandomBit() if err != nil { return nil, err } if entropyBit { payload[0] = 1 } var raw bytes.Buffer responsePublicHeader := PublicHeader{ ConnectionID: p.connectionID, PacketNumber: currentPacketNumber, } if err := responsePublicHeader.WritePublicHeader(&raw); err != nil { return nil, err } ciphertext := p.aead.Seal(currentPacketNumber, raw.Bytes(), payload) raw.Write(ciphertext) if raw.Len() > protocol.MaxPacketSize { panic("internal inconsistency: packet too large") } return &packedPacket{ number: currentPacketNumber, entropyBit: entropyBit, raw: raw.Bytes(), payload: payload[1:], }, nil } func (p *packetPacker) composeNextPayload(currentPacketNumber protocol.PacketNumber) ([]byte, error) { var payload bytes.Buffer payload.WriteByte(0) // The entropy bit is set in sendPayload for len(p.queuedFrames) > 0 { frame := p.queuedFrames[0] if payload.Len()-1 > protocol.MaxFrameSize { panic("internal inconsistency: packet payload too large") } // Does the frame fit into the remaining space? if payload.Len()-1+frame.MaxLength() > protocol.MaxFrameSize { return payload.Bytes(), nil } if streamframe, isStreamFrame := frame.(*frames.StreamFrame); isStreamFrame { // Split stream frames if necessary previousFrame := streamframe.MaybeSplitOffFrame(protocol.MaxFrameSize - (payload.Len() - 1)) if previousFrame != nil { // Don't pop the queue, leave the modified frame in frame = previousFrame } else { p.queuedFrames = p.queuedFrames[1:] } } else { p.queuedFrames = p.queuedFrames[1:] } if err := frame.Write(&payload, currentPacketNumber, 6); err != nil { return nil, err } } return payload.Bytes(), nil }