github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/quic/gquic-go/framer.go (about) 1 package gquic 2 3 import ( 4 "sync" 5 6 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic/gquic-go/internal/protocol" 7 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic/gquic-go/internal/wire" 8 ) 9 10 type framer struct { 11 streamGetter streamGetter 12 cryptoStream cryptoStream 13 version protocol.VersionNumber 14 15 streamQueueMutex sync.Mutex 16 activeStreams map[protocol.StreamID]struct{} 17 streamQueue []protocol.StreamID 18 19 controlFrameMutex sync.Mutex 20 controlFrames []wire.Frame 21 } 22 23 func newFramer( 24 cryptoStream cryptoStream, 25 streamGetter streamGetter, 26 v protocol.VersionNumber, 27 ) *framer { 28 return &framer{ 29 streamGetter: streamGetter, 30 cryptoStream: cryptoStream, 31 activeStreams: make(map[protocol.StreamID]struct{}), 32 version: v, 33 } 34 } 35 36 func (f *framer) QueueControlFrame(frame wire.Frame) { 37 f.controlFrameMutex.Lock() 38 f.controlFrames = append(f.controlFrames, frame) 39 f.controlFrameMutex.Unlock() 40 } 41 42 func (f *framer) AppendControlFrames(frames []wire.Frame, maxLen protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) { 43 var length protocol.ByteCount 44 f.controlFrameMutex.Lock() 45 for len(f.controlFrames) > 0 { 46 frame := f.controlFrames[len(f.controlFrames)-1] 47 frameLen := frame.Length(f.version) 48 if length+frameLen > maxLen { 49 break 50 } 51 frames = append(frames, frame) 52 length += frameLen 53 f.controlFrames = f.controlFrames[:len(f.controlFrames)-1] 54 } 55 f.controlFrameMutex.Unlock() 56 return frames, length 57 } 58 59 // AddActiveStream adds a stream that has data to write. 60 // It should not be used for the crypto stream. 61 func (f *framer) AddActiveStream(id protocol.StreamID) { 62 f.streamQueueMutex.Lock() 63 if _, ok := f.activeStreams[id]; !ok { 64 f.streamQueue = append(f.streamQueue, id) 65 f.activeStreams[id] = struct{}{} 66 } 67 f.streamQueueMutex.Unlock() 68 } 69 70 func (f *framer) AppendStreamFrames(frames []wire.Frame, maxLen protocol.ByteCount) []wire.Frame { 71 var length protocol.ByteCount 72 f.streamQueueMutex.Lock() 73 // pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet 74 numActiveStreams := len(f.streamQueue) 75 for i := 0; i < numActiveStreams; i++ { 76 if maxLen-length < protocol.MinStreamFrameSize { 77 break 78 } 79 id := f.streamQueue[0] 80 f.streamQueue = f.streamQueue[1:] 81 // This should never return an error. Better check it anyway. 82 // The stream will only be in the streamQueue, if it enqueued itself there. 83 str, err := f.streamGetter.GetOrOpenSendStream(id) 84 // The stream can be nil if it completed after it said it had data. 85 if str == nil || err != nil { 86 delete(f.activeStreams, id) 87 continue 88 } 89 frame, hasMoreData := str.popStreamFrame(maxLen - length) 90 if hasMoreData { // put the stream back in the queue (at the end) 91 f.streamQueue = append(f.streamQueue, id) 92 } else { // no more data to send. Stream is not active any more 93 delete(f.activeStreams, id) 94 } 95 if frame == nil { // can happen if the receiveStream was canceled after it said it had data 96 continue 97 } 98 frames = append(frames, frame) 99 length += frame.Length(f.version) 100 } 101 f.streamQueueMutex.Unlock() 102 return frames 103 }