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
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package doppel
  2. import (
  3. "math"
  4. "math/rand/v2"
  5. "time"
  6. )
  7. const (
  8. StatsBisectTimes = 70
  9. StatsLowK = 0.01
  10. StatsHighK = 10.0
  11. // do not calculate statistics if we have < than this number of durations
  12. MinDurationsToCalculate = 100
  13. // these values are taken from ok.ru. measured from moscow site.
  14. StatsDefaultK = 0.37846373895785335
  15. StatsDefaultLambda = 1.73177086015485
  16. // how many bytes should we drift
  17. DRSNoise = 100
  18. )
  19. // Stats is responsible for generating values that are distributed according
  20. // to some statistical distribution.
  21. //
  22. // It follows several ideas:
  23. // 1. Based on nginx and Cloudflare behaviour, even if server is eager
  24. // to send a lot, they all start with small TLS packets that are
  25. // approximately MTU-sized. After
  26. // 2. After ~40 TLS records, server considers TCP session as somewhat solid
  27. // and reliable and ramps up to 4096.
  28. // 3. After ~20 TLS records more it jumps to the max 16384 bytes and keep
  29. // this size as long as it can
  30. // 4. If there is no any byte within a connection for a longer time period,
  31. // this counter resets.
  32. //
  33. // This is called Dynamic TLS Record Sizing
  34. // - https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/
  35. // - https://community.f5.com/kb/technicalarticles/boosting-tls-performance-with-dynamic-record-sizing-on-big-ip/280798
  36. // - https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
  37. //
  38. // And this optimized for the very first byte, so web browsers could start to
  39. // render as early as possible, showing user some preliminary results, optimizing
  40. // for perceived latency.
  41. //
  42. // Since this is very typical for the website, we also aim for that.
  43. //
  44. // Another important idea is how delays between TLS packets are distributed.
  45. // In case of sending huge heavy content with max sized record, delays have
  46. // lognormal distribution. But a nature of a typical website shows that
  47. // it eagers to deliver as fast as it can in a few very first records and
  48. // could possibly slow down later.
  49. //
  50. // This is perfectly described by Weibull distribution:
  51. // - https://en.wikipedia.org/wiki/Weibull_distribution
  52. // - https://ieeexplore.ieee.org/document/6662948
  53. // - https://www.researchgate.net/publication/224621285_Traffic_modelling_and_cost_optimization_for_transmitting_traffic_messages_over_a_hybrid_broadcast_and_cellular_network
  54. // - https://ir.uitm.edu.my/id/eprint/105386/1/105386.pdf
  55. //
  56. // In other word, a combination of Dynamic TLS Record Sizing hints us for
  57. // Weibull distribution.
  58. type Stats struct {
  59. sizeLastRequested time.Time
  60. sizeCounter int
  61. // https://en.wikipedia.org/wiki/Shape_parameter
  62. k float64
  63. // https://en.wikipedia.org/wiki/Scale_parameter
  64. lambda float64
  65. // Dynamic Record Sizing
  66. drs bool
  67. }
  68. func (d *Stats) Delay() time.Duration {
  69. // u ∈ (0, 1], avoids ln(0)
  70. u := 1.0 - rand.Float64()
  71. // X = λ·(-ln U)^(1/k)
  72. generated := d.lambda * math.Pow(-math.Log(u), 1.0/d.k)
  73. // generated is in milliseconds
  74. return time.Duration(generated * float64(time.Millisecond))
  75. }
  76. func (d *Stats) Size() int {
  77. if time.Since(d.sizeLastRequested) > TLSRecordSizeResetAfter {
  78. d.sizeCounter = 0
  79. }
  80. if !d.drs {
  81. return TLSRecordSizeMax
  82. }
  83. d.sizeLastRequested = time.Now()
  84. d.sizeCounter++
  85. switch {
  86. case d.sizeCounter <= TLSCounterAccelAfter:
  87. return TLSRecordSizeStart - rand.IntN(DRSNoise)
  88. case d.sizeCounter <= TLSCounterMaxAfter:
  89. return TLSRecordSizeAccel - rand.IntN(DRSNoise)
  90. }
  91. return TLSRecordSizeMax
  92. }
  93. func NewStats(durations []time.Duration, drs bool) *Stats {
  94. n := float64(len(durations))
  95. // in milliseconds
  96. durFloats := make([]float64, len(durations))
  97. for i, v := range durations {
  98. durFloats[i] = float64(v.Microseconds()) / 1000.0
  99. }
  100. // The bisection solves the standard Weibull MLE equation for shape
  101. // parameter k. There is no any good formula for doing that so we
  102. // approximate it by several bisections. The number of operations
  103. // is statically defined by a constant.
  104. sumLog := 0.0
  105. for _, v := range durFloats {
  106. sumLog += math.Log(v)
  107. }
  108. lowK := StatsLowK
  109. highK := StatsHighK
  110. for range StatsBisectTimes {
  111. midK := (lowK + highK) / 2.0
  112. sumXK := 0.0
  113. sumXKLog := 0.0
  114. for _, v := range durFloats {
  115. xk := math.Pow(v, midK)
  116. sumXK += xk
  117. sumXKLog += xk * math.Log(v)
  118. }
  119. if (1.0/midK)+(sumLog/n)-(sumXKLog/sumXK) > 0 {
  120. lowK = midK
  121. } else {
  122. highK = midK
  123. }
  124. }
  125. k := (lowK + highK) / 2
  126. sumXK := 0.0
  127. for _, v := range durFloats {
  128. sumXK += math.Pow(v, k)
  129. }
  130. // λ = (Σxᵢᵏ / n)^(1/k)
  131. lambda := math.Pow(sumXK/n, 1.0/k)
  132. return &Stats{
  133. k: k,
  134. lambda: lambda,
  135. drs: drs,
  136. }
  137. }