Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ganger.go 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package doppel
  2. import (
  3. "context"
  4. "sync"
  5. "time"
  6. "github.com/9seconds/mtg/v2/essentials"
  7. )
  8. const (
  9. DoppelGangerMaxDurations = 4096
  10. DoppelGangerScoutRaidEach = 6 * time.Hour
  11. DoppelGangerScoutRepeats = 10
  12. )
  13. type gangerConnRequest struct {
  14. ret chan<- Conn
  15. payload essentials.Conn
  16. }
  17. type Ganger struct {
  18. ctx context.Context
  19. ctxCancel context.CancelFunc
  20. logger Logger
  21. wg sync.WaitGroup
  22. scout Scout
  23. scoutRaidEach time.Duration
  24. scoutRaidRepeats int
  25. stats *Stats
  26. durations []time.Duration
  27. connRequests chan gangerConnRequest
  28. }
  29. func (g *Ganger) Shutdown() {
  30. g.ctxCancel()
  31. g.wg.Wait()
  32. }
  33. func (g *Ganger) Run() {
  34. g.wg.Go(func() {
  35. g.run()
  36. })
  37. }
  38. func (g *Ganger) NewConn(conn essentials.Conn) (Conn, error) {
  39. rvChan := make(chan Conn)
  40. req := gangerConnRequest{
  41. ret: rvChan,
  42. payload: conn,
  43. }
  44. defer close(req.ret)
  45. select {
  46. case <-g.ctx.Done():
  47. return Conn{}, context.Cause(g.ctx)
  48. case g.connRequests <- req:
  49. }
  50. select {
  51. case <-g.ctx.Done():
  52. return Conn{}, context.Cause(g.ctx)
  53. case conn := <-rvChan:
  54. return conn, nil
  55. }
  56. }
  57. func (g *Ganger) run() {
  58. scoutTicker := time.NewTicker(g.scoutRaidEach)
  59. defer func() {
  60. scoutTicker.Stop()
  61. select {
  62. case <-scoutTicker.C:
  63. default:
  64. }
  65. }()
  66. scoutCollectedChan := make(chan []time.Duration)
  67. currentScoutCollectedChan := scoutCollectedChan
  68. updatedStatsChan := make(chan *Stats)
  69. g.wg.Go(func() {
  70. g.runScoutRaid(scoutCollectedChan)
  71. })
  72. for {
  73. select {
  74. case <-g.ctx.Done():
  75. return
  76. case durations := <-currentScoutCollectedChan:
  77. g.durations = append(g.durations, durations...)
  78. if len(g.durations) > DoppelGangerMaxDurations {
  79. g.durations = g.durations[len(g.durations)-DoppelGangerMaxDurations:]
  80. }
  81. if len(g.durations) < MinDurationsToCalculate {
  82. continue
  83. }
  84. currentScoutCollectedChan = nil
  85. g.wg.Go(func() {
  86. select {
  87. case <-g.ctx.Done():
  88. case updatedStatsChan <- NewStats(durations):
  89. }
  90. })
  91. case stats := <-updatedStatsChan:
  92. g.stats = stats
  93. currentScoutCollectedChan = scoutCollectedChan
  94. case <-scoutTicker.C:
  95. g.wg.Go(func() {
  96. g.runScoutRaid(scoutCollectedChan)
  97. })
  98. case req := <-g.connRequests:
  99. select {
  100. case <-g.ctx.Done():
  101. case req.ret <- NewConn(g.ctx, req.payload, g.stats):
  102. }
  103. }
  104. }
  105. }
  106. func (g *Ganger) runScoutRaid(rvChan chan<- []time.Duration) {
  107. durations := []time.Duration{}
  108. for range g.scoutRaidRepeats {
  109. learned, err := g.scout.Learn(g.ctx)
  110. if err != nil {
  111. g.logger.WarningError("cannot learn", err)
  112. continue
  113. }
  114. durations = append(durations, learned...)
  115. }
  116. select {
  117. case <-g.ctx.Done():
  118. return
  119. case rvChan <- durations:
  120. }
  121. }
  122. func NewGanger(
  123. ctx context.Context,
  124. network Network,
  125. logger Logger,
  126. scoutEach time.Duration,
  127. scoutRepeats int,
  128. urls []string,
  129. ) *Ganger {
  130. ctx, cancel := context.WithCancel(ctx)
  131. if scoutEach == 0 {
  132. scoutEach = DoppelGangerScoutRaidEach
  133. }
  134. if scoutRepeats == 0 {
  135. scoutRepeats = DoppelGangerScoutRepeats
  136. }
  137. return &Ganger{
  138. ctx: ctx,
  139. ctxCancel: cancel,
  140. logger: logger,
  141. scoutRaidEach: scoutEach,
  142. scoutRaidRepeats: scoutRepeats,
  143. stats: &Stats{
  144. k: StatsDefaultK,
  145. lambda: StatsDefaultLambda,
  146. },
  147. scout: NewScout(network, urls),
  148. connRequests: make(chan gangerConnRequest),
  149. }
  150. }