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 }