github.com/ooni/psiphon/tunnel-core@v0.0.0-20230105123940-fe12a24c96ee/oovendor/quic-go/closed_session.go (about) 1 package quic 2 3 import ( 4 "sync" 5 6 "github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/protocol" 7 "github.com/ooni/psiphon/tunnel-core/oovendor/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. 13 type 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 28 var _ packetHandler = &closedLocalSession{} 29 30 // newClosedLocalSession creates a new closedLocalSession and runs it. 31 func 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 49 func (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 60 func (s *closedLocalSession) handlePacket(p *receivedPacket) { 61 select { 62 case s.receivedPackets <- p: 63 default: 64 } 65 } 66 67 func (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 82 func (s *closedLocalSession) shutdown() { 83 s.destroy(nil) 84 } 85 86 func (s *closedLocalSession) destroy(error) { 87 s.closeOnce.Do(func() { 88 close(s.closeChan) 89 }) 90 } 91 92 func (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. 99 type closedRemoteSession struct { 100 perspective protocol.Perspective 101 } 102 103 var _ packetHandler = &closedRemoteSession{} 104 105 func newClosedRemoteSession(pers protocol.Perspective) packetHandler { 106 return &closedRemoteSession{perspective: pers} 107 } 108 109 func (s *closedRemoteSession) handlePacket(*receivedPacket) {} 110 func (s *closedRemoteSession) shutdown() {} 111 func (s *closedRemoteSession) destroy(error) {} 112 func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective }