1package quic 2 3import ( 4 "sync" 5 6 "github.com/lucas-clemente/quic-go/internal/protocol" 7 "github.com/lucas-clemente/quic-go/internal/utils" 8) 9 10// A closedLocalSession is a session that we closed locally. 11// When receiving packets for such a session, we need to retransmit the packet containing the CONNECTION_CLOSE frame, 12// with an exponential backoff. 13type closedLocalSession struct { 14 conn sendConn 15 connClosePacket []byte 16 17 closeOnce sync.Once 18 closeChan chan struct{} // is closed when the session is closed or destroyed 19 20 receivedPackets chan *receivedPacket 21 counter uint64 // number of packets received 22 23 perspective protocol.Perspective 24 25 logger utils.Logger 26} 27 28var _ packetHandler = &closedLocalSession{} 29 30// newClosedLocalSession creates a new closedLocalSession and runs it. 31func newClosedLocalSession( 32 conn sendConn, 33 connClosePacket []byte, 34 perspective protocol.Perspective, 35 logger utils.Logger, 36) packetHandler { 37 s := &closedLocalSession{ 38 conn: conn, 39 connClosePacket: connClosePacket, 40 perspective: perspective, 41 logger: logger, 42 closeChan: make(chan struct{}), 43 receivedPackets: make(chan *receivedPacket, 64), 44 } 45 go s.run() 46 return s 47} 48 49func (s *closedLocalSession) run() { 50 for { 51 select { 52 case p := <-s.receivedPackets: 53 s.handlePacketImpl(p) 54 case <-s.closeChan: 55 return 56 } 57 } 58} 59 60func (s *closedLocalSession) handlePacket(p *receivedPacket) { 61 select { 62 case s.receivedPackets <- p: 63 default: 64 } 65} 66 67func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) { 68 s.counter++ 69 // exponential backoff 70 // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving 71 for n := s.counter; n > 1; n = n / 2 { 72 if n%2 != 0 { 73 return 74 } 75 } 76 s.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", s.counter) 77 if err := s.conn.Write(s.connClosePacket); err != nil { 78 s.logger.Debugf("Error retransmitting CONNECTION_CLOSE: %s", err) 79 } 80} 81 82func (s *closedLocalSession) shutdown() { 83 s.destroy(nil) 84} 85 86func (s *closedLocalSession) destroy(error) { 87 s.closeOnce.Do(func() { 88 close(s.closeChan) 89 }) 90} 91 92func (s *closedLocalSession) getPerspective() protocol.Perspective { 93 return s.perspective 94} 95 96// A closedRemoteSession is a session that was closed remotely. 97// For such a session, we might receive reordered packets that were sent before the CONNECTION_CLOSE. 98// We can just ignore those packets. 99type closedRemoteSession struct { 100 perspective protocol.Perspective 101} 102 103var _ packetHandler = &closedRemoteSession{} 104 105func newClosedRemoteSession(pers protocol.Perspective) packetHandler { 106 return &closedRemoteSession{perspective: pers} 107} 108 109func (s *closedRemoteSession) handlePacket(*receivedPacket) {} 110func (s *closedRemoteSession) shutdown() {} 111func (s *closedRemoteSession) destroy(error) {} 112func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective } 113